﻿#include "myapp.h"
#include "mytechframe.h"
#include "mywebframe.h"
#include "mylogindlg.h"
#include "mylogdlg.h"
#include "mysettingsdlg.h"
#include "mysmartkbdlg.h"
#include <techdlg.h>
#include <zqdb.pb.h>

MyCodeView::MyCodeView(wxWindow* parent, const char* xml, size_t xmlflag) : Base(parent, xml, xmlflag)
{
	auto frame = MyTechFrame::GetFrameByChild(this);

	auto sizerTop = new wxBoxSizer(wxVERTICAL);

	ctrl_text_ = new wxTextCtrl(this, wxID_EDIT, wxEmptyString, wxDefaultPosition, wxDefaultSize
		, /*wxNO_BORDER | */wxTE_PROCESS_ENTER | wxTE_PROCESS_TAB);
	ctrl_text_->Hide();
	sizerTop->Add(ctrl_text_, 0, wxEXPAND);

	//sizerTop->AddSpacer(2);

	ctrl_list_ = new MyScrollT<wxDataViewCtrl>(this, wxID_ANY, wxDefaultPosition, wxDefaultSize
		, wxNO_BORDER | wxDV_NO_HEADER | wxDV_VARIABLE_LINE_HEIGHT);
	auto vscrollbar = new MyScrollBar(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSB_VERTICAL);
	((MyScrollT<wxDataViewCtrl>*)ctrl_list_)->SetScrollBar(nullptr, vscrollbar);
#ifdef _DEBUG
	ctrl_list_->ShowScrollbars(wxSHOW_SB_NEVER, wxSHOW_SB_ALWAYS);
#else
	ctrl_list_->ShowScrollbars(wxSHOW_SB_NEVER, wxSHOW_SB_DEFAULT);
#endif

	ctrl_list_model_ = new MyCodeViewListModel;
	ctrl_list_->AssociateModel(ctrl_list_model_.get());

	ctrl_list_render_ = new MyCodeViewListRenderer(this);
	auto code_col = new wxDataViewColumn("Code", ctrl_list_render_, MyCodeViewListModel::Col_Code
		, 80, wxALIGN_LEFT, wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_COL_SORTABLE);
	ctrl_list_->AppendColumn(code_col);
	//ctrl_list_->AppendTextColumn("Code",
	//	SmartKBListModel::Col_Code);

	//ctrl_list_model_->ShowAll();

	auto sizerList = new wxBoxSizer(wxHORIZONTAL);
	sizerList->Add(ctrl_list_, 1, wxEXPAND);
#ifdef _DEBUG
	auto sizerSide = new wxBoxSizer(wxVERTICAL);
	auto btn_expand = new wxButton(this, wxID_ANY, frame->IsExpand() ? wxT("◀") : wxT("▶")
		, wxDefaultPosition, wxSize(vscrollbar->GetSize().x, vscrollbar->GetSize().x));
	btn_expand->Bind(wxEVT_BUTTON, [this, frame, btn_expand](wxCommandEvent& evt) {
		frame->ShowExpand();
		btn_expand->SetLabel(frame->IsExpand() ? wxT("◀") : wxT("▶"));
	});
	sizerSide->Add(btn_expand, 0, wxEXPAND);
	sizerSide->Add(vscrollbar, 1, wxEXPAND);
	sizerList->Add(sizerSide, 0, wxEXPAND);
#else
	sizerList->Add(vscrollbar, 0, wxEXPAND);
#endif
	sizerTop->Add(sizerList, wxSizerFlags(1).Expand());
	//sizerTop->Add(ctrl_list_, 1, wxEXPAND);

	SetSizer(sizerTop);

	Bind(SMARTKB_SEARCH_RESULT_EVENT, &MyCodeView::OnSearchResult, this);
	Bind(wxEVT_DATAVIEW_ITEM_ACTIVATED, &MyCodeView::OnActivated, this);
	Bind(wxEVT_DATAVIEW_SELECTION_CHANGED, &MyCodeView::OnSelChanged, this);
	Bind(wxEVT_DATAVIEW_ITEM_CONTEXT_MENU, &MyCodeView::OnContextMenu, this);

	//SetSize(FromDIP(wxSize(200, 300)));
}

MyCodeView::~MyCodeView()
{

}

wxSize MyCodeView::DoGetBestSize() const
{
	return FromDIP(wxSize(200, 300));
}

zqdb::Calc::Container MyCodeView::Cur(bool origin)
{
	return ctrl_list_model_->Cur(origin);
}

size_t MyCodeView::Count() const
{
	return ctrl_list_model_->GetCount();
}

bool MyCodeView::IsShowAll() const
{
	if (pr_container_.second) {
		return false;
	}
	return ctrl_list_model_->IsShowAll();
}

bool MyCodeView::IsShowContainer() const
{
	if (pr_container_.second) {
		return true;
	}
	return ctrl_list_model_->IsShowContainer();
}

bool MyCodeView::IsShowSearch() const
{
	if (IsShowAll()) {
		return false;
	}
	else if (IsShowContainer()) {
		return false;
	}
	else {
		return true;
	}
}

bool MyCodeView::IsShowSearch(const wxString& key) const 
{ 
	if (IsShowAll()) {
		return false;
	}
	else if (IsShowContainer()) {
		return false;
	}
	else {
		return key == ctrl_text_->GetValue();
	}
}

bool MyCodeView::IsTextSearching() const
{
	return ctrl_text_->FindFocus() == ctrl_text_;
}

int MyCodeView::FilterEvent(wxEvent& event)
{
	const wxEventType t = event.GetEventType();
	auto obj = event.GetEventObject();
	if (obj) {
		/*if (t == wxEVT_KEY_DOWN) {
			if (obj == ctrl_text_) {
				auto key_event = wxDynamicCast(&event, wxKeyEvent);
				auto key_code = key_event->GetKeyCode();
				if (obj == ctrl_text_) {
					switch (key_code)
					{
					case WXK_UP:
					case WXK_DOWN:
					case WXK_RETURN: {
						DoAction(key_code);
						ctrl_list_->SetFocus();
						return wxEventFilter::Event_Ignore;
					} break;
					default:
						break;
					}
				}
			}
			else {
				if (!wxDynamicCast(obj, wxTextCtrl) || wxDynamicCast(obj, wxRichTextCtrl) || wxDynamicCast(obj, wxStyledTextCtrl)) {
					auto key_event = wxDynamicCast(&event, wxKeyEvent);
					auto key_code = key_event->GetKeyCode();
					if (key_code >= 0 && key_code <= 255) {
						if (isalnum(key_code)) {
							if (IsShowContainer()) {
								InnerShowKey(wxString::Format(wxT("%c"), key_code));
							}
							else {
								ctrl_text_->SetValue(wxString::Format(wxT("%c"), key_code));
							}
							ctrl_text_->SetFocus();
							ctrl_text_->SetSelection(1, 1);
							return wxEventFilter::Event_Ignore;
						}
						else {
							switch (key_code)
							{
							case WXK_BACK: {
								auto label = ctrl_text_->GetValue();
								if (!label.empty() && label != wxT(SMARTKB_KEY_SUBSCRIBE)) {
									label.RemoveLast();
									ctrl_text_->SetFocus();
									ctrl_text_->SetValue(label);
									//ctrl_text_->SetSelection(label.size(), -1);
									ctrl_text_->SetInsertionPoint(-1);
									return wxEventFilter::Event_Ignore;
								}
							} break;
							default:
								break;
							}
						}
					}
				}
				}
		}
		else */if (t == wxEVT_MOUSEWHEEL) {
			auto wnd = wxDynamicCast(obj, wxWindow);
			if (wnd && ctrl_list_) {
				auto mouse_event = wxDynamicCast(&event, wxMouseEvent);
				auto pt = wnd->ClientToScreen(mouse_event->GetPosition());
				auto rc = ctrl_list_->GetScreenRect();
				if (rc.Contains(pt)) {
					ctrl_list_->GetEventHandler()->ProcessEvent(event);
					return wxEventFilter::Event_Ignore;
				}
			}
		}
	}
	// Continue processing the event normally as well.
	return wxEventFilter::Event_Skip;
}

void MyCodeView::DoSelect(const wxDataViewItem& item, size_t try_count)
{
	auto pos = ctrl_list_model_->GetRow(item);
	ctrl_list_->Select(item);
	ctrl_list_->EnsureVisible(item);
	/*auto lines = ctrl_list_->GetScrollLines(wxVERTICAL);
	auto offset = pos - lines;
	ctrl_list_->ScrollLines(offset);*/
	int count = ctrl_list_model_->GetCount();
	int range = ctrl_list_->GetScrollRange(wxVERTICAL);
	if (range <= 1) {
		if (try_count > 1) {
			return;
		}
		wxGetApp().Post(500, [this, item, try_count]() {
			DoSelect(item, try_count + 1);
		});
		/*auto count_per_page = ctrl_list_->GetCountPerPage();
		auto real_count_per_page = count_per_page / 2; //这里代码显示占用了两行位置
		if (pos + real_count_per_page >= count) {
			ctrl_list_->EnsureVisible(ctrl_list_model_->GetItem(count - 1));
		}
		else {
			ctrl_list_->EnsureVisible(ctrl_list_model_->GetItem(pos + real_count_per_page));
		}*/
		//ctrl_list_->Refresh();
		//ctrl_list_->Update();
		//range = ctrl_list_->GetScrollRange(wxVERTICAL);
	}
	else {
		//range = ctrl_list_render_->GetSize().y * count;
		int scrollPos = pos * range / count;
		ctrl_list_->Scroll(0, scrollPos);
	}
	/*auto rc = ctrl_list_->GetItemRect(item);
	if (rc.IsEmpty()) {
	}
	else if (rc.y < 0) {
		ctrl_list_->LineUp();
	}*/
}

bool MyCodeView::DoSelect(HZQDB h, wxDataViewItem& item)
{
	auto pos = ctrl_list_model_->FindResult(h);
	if (pos >= 0) {
		item = ctrl_list_model_->GetItem(pos);
		DoSelect(item);
		return true;
	}
	return false;
}

void MyCodeView::DoSelect(int pos)
{
	if (pos >= 0) {
		auto item = ctrl_list_model_->GetItem(pos);
		DoSelect(item);
	}
}

void MyCodeView::DoGoto(int pos)
{
	if (pos >= 0) {
		auto item = ctrl_list_model_->GetItem(pos);
		if (item) {
			DoSelect(item);
			Activate(item, false);
		}
	}
}

int MyCodeView::Find(HZQDB h)
{
	return ctrl_list_model_->FindResult(h);
}

bool MyCodeView::Select(HZQDB h, bool bEnsureVisible)
{
	if (bEnsureVisible) {
		wxDataViewItem item;
		return DoSelect(h, item);
	}
	else {
		auto pos = ctrl_list_model_->FindResult(h);
		if (pos >= 0) {
			auto item = ctrl_list_model_->GetItem(pos);
			if (item != ctrl_list_->GetSelection()) {
				ctrl_list_->Select(item);
			}
			return true;
		}
	}
	return false;
}

void MyCodeView::Redo(bool all)
{
	if (IsShowContainer()) {
		if (all) {
			pr_container_.second.Calc();
		}
		ctrl_list_model_->Search(this, pr_container_.second);
	}
	else if (IsShowAll()) {
		ctrl_list_model_->Search(this, wxEmptyString);
	}
	else {
		ctrl_list_model_->Search(this, ctrl_text_->GetValue());
	}
}

bool MyCodeView::Goto(HZQDB h)
{
	ASSERT(h && h->type == ZQDB_HANDLE_TYPE_CODE);
	wxDataViewItem item;
	if (DoSelect(h, item)) {
		Activate(item);
		return true;
	}
	//if (!IsShowSearch(wxT(SMARTKB_KEY_SUBSCRIBE))) {
	//	ShowKey(wxT(SMARTKB_KEY_SUBSCRIBE));
	//	wxGetApp().Post(200, [this, h]() {
	//		Goto(h);
	//	});
	//}
	///*if (!ctrl_list_model_->IsShowAll()) {
	//	ShowKey(wxT(SMARTKB_KEY_ALL));
	//	wxGetApp().Post(200, [this,h]() {
	//		Goto(h);
	//	});
	//}*/
	//else {
	//	DoGoto(h);
	//}
	return false;
}

void MyCodeView::InnerShowKey(const wxString& key, HZQDB sel)
{
	sel_ = sel;
	pr_container_.first.Clear();
	pr_container_.second.Close();
	if (key == wxT(SMARTKB_KEY_ALL)) {
		ctrl_text_->Clear();
		//ctrl_list_model_->ShowAll();
	}
	else {
		//比如筛选”主力“
		ctrl_text_->SetValue(key);
	}
}

void MyCodeView::ShowKey(const wxString& key, HZQDB sel)
{
	InnerShowKey(key, sel);
	ctrl_list_->SetFocus();
}

void MyCodeView::ShowContainer(const wxString& key, const zqdb::Calc::Container& container, HZQDB sel)
{
	sel_ = sel;
	pr_container_.first = key;
	pr_container_.second = container;
	ctrl_text_->SetValue(key);
}

wxString MyCodeView::GetKey(zqdb::Calc::Container& container)
{ 
	if (IsShowContainer()) {
		container = pr_container_.second;
		return pr_container_.first;
	}
	return ctrl_text_->GetValue();
}

void MyCodeView::Up()
{
	/*auto pos = GetCurItemPos();
	if (pos <= 0) {
		pos = GetItemCount() - 1;
	}
	else {
		pos = (pos - 1) % GetItemCount();
	}
	SetCurItemPos(pos);*/
}

void MyCodeView::Down()
{
	/*auto pos = GetCurItemPos();
	if (pos >= (GetItemCount() - 1)) {
		pos = 0;
	}
	else {
		pos = (pos + 1) % GetItemCount();
	}
	SetCurItemPos(pos);*/
}

int MyCodeView::IsSort(MY_CODE_SORT_TYPE* type, size_t* secs)
{
	return ctrl_list_model_->IsSort(type, secs);
}

void MyCodeView::SortByZD(MY_CODE_SORT_TYPE type, size_t duration, int sort)
{
	wxDataViewColumn* const col = ctrl_list_->GetColumn(0);
	if (sort)
		col->SetSortOrder(sort < 0 ? true /* ascending */ : false);
	else
		col->UnsetAsSortKey();

	ctrl_list_model_->SortByZD(type, duration, sort);
	DoGoto(0);
	DoRefresh(true);
}

void MyCodeView::SortByField(MDB_FIELD& field, int sort)
{
	ctrl_list_model_->SortByField(field, sort);
	DoGoto(0);
	DoRefresh(true);
}

void MyCodeView::SortByCalc(const zqdb::Calc::Sort& calc, int sort)
{
	ctrl_list_model_->SortByCalc(calc, sort);
	DoGoto(0);
	DoRefresh(true);
}

void MyCodeView::OnSkinInfoChanged()
{
	//Base::OnSkinInfoChanged();
	auto skin_info_ptr = std::static_pointer_cast<SkinInfo>(skin_info_ptr_);
	ctrl_list_->SetBackgroundColour(skin_info_ptr->crCtrlBkgnd);
	ctrl_list_->SetForegroundColour(skin_info_ptr->crCtrlForgnd);
	ctrl_list_render_->SetSkinInfo(skin_info_ptr);
	wxSize cellSize = ctrl_list_render_->GetSize();
	ctrl_list_->SetScrollRate(0, cellSize.y);
}

void MyCodeView::OnHandleChanged()
{
	Base::OnHandleChanged();
}

void MyCodeView::DoUpdate(bool all)
{
	thread_local zqdb::TaskID task_id;
	if (task_id) {
		wxGetApp().Cancel(task_id);
		task_id.reset();
	}
	task_id = wxGetApp().Post(100, [this, all] {
		task_id.reset();
		Redo(all);
	});
}

void  MyCodeView::OnNotifyStatus(HZQDB h)
{
	switch (h->type)
	{
	case ZQDB_HANDLE_TYPE_CODE: {
		if(IsShowSearch(wxT(SMARTKB_KEY_SELFSEL)) || IsShowSearch(wxT(SMARTKB_KEY_SUBSCRIBE))) {
			if (Find(h) < 0) {
				DoUpdate();
			}
		}
	} break;
	case ZQDB_HANDLE_TYPE_MODULE: {
		if (ZQDBIsDisable(h))
			;
		else
			DoUpdate(true);
	} break;
	}
}

void MyCodeView::OnNotifyAdd(HZQDB h)
{
	/*if (h->type == ZQDB_HANDLE_TYPE_EXCHANGE) {
		zqdb::Exchange exchange(h);
		LOG4D("UpdateAll %s", exchange->Exchange);
		UpdateAll();
	}*/
}

void MyCodeView::OnNotifyUpdate(HZQDB h)
{
	//if (h->type == ZQDB_HANDLE_TYPE_CODE) {
	//	auto pos = ctrl_list_model_->FindResult(h);
	//	auto item = ctrl_list_model_->GetItem(pos);
	//	auto rect = ctrl_list_->GetItemRect(item);
	//	ctrl_list_->RefreshRect(rect);
	//	/*auto count_per_page = ctrl_list_->GetCountPerPage();
	//	if (pos < count_per_page) {
	//	}*/
	//}
}

void MyCodeView::DoAction(int key)
{
	switch (key)
	{
	case WXK_UP: {
		auto sel = ctrl_list_->GetSelection();
		if (sel) {
			auto sel_row = ctrl_list_model_->GetRow(sel);
			if (sel_row > 0) {
				sel = ctrl_list_model_->GetItem(sel_row - 1);
				ctrl_list_->Select(sel);
				ctrl_list_->EnsureVisible(sel);
			}
			Activate(sel);
		}
		else {
			if (ctrl_list_model_->GetCount()) {
				sel = ctrl_list_model_->GetItem(0);
				ctrl_list_->Select(sel);
				Activate(sel);
			}
		}
	} break;
	case WXK_DOWN: {
		auto sel = ctrl_list_->GetSelection();
		if (sel) {
			auto sel_row = ctrl_list_model_->GetRow(sel);
			if (sel_row < (ctrl_list_model_->GetCount() - 1)) {
				sel = ctrl_list_model_->GetItem(sel_row + 1);
				ctrl_list_->Select(sel);
				ctrl_list_->EnsureVisible(sel);
			}
			Activate(sel);
		}
		else {
			if (ctrl_list_model_->GetCount()) {
				sel = ctrl_list_model_->GetItem(0);
				ctrl_list_->Select(sel);
				Activate(sel);
			}
		}
	} break;
	case WXK_RETURN: {
		auto sel = ctrl_list_->GetSelection();
		Activate(sel);
	} break;
	default:
		break;
	}
}

void MyCodeView::DoRefresh(bool first)
{
	ctrl_list_->Refresh();
	if (!ZQDBIsRPC()) {
		return;
	}
	if (IsShowSearch(wxT(SMARTKB_KEY_SUBSCRIBE))) {
		return;
	}
	auto count = ctrl_list_model_->GetCount();
	if (count <= 0) {
		return;
	}
	MY_CODE_SORT_TYPE sort_type;
	auto sort = IsSort(&sort_type);
	bool req_all = sort && sort_type == SORT_ZDF && IsShowSearch(wxT(SMARTKB_KEY_SELFSEL));
	auto now = std::chrono::system_clock::now();
	auto span = std::chrono::duration_cast<std::chrono::milliseconds>(now - tp_refresh_);
	if (first) {
		cnt_refresh_ = 0;
	}
	else if (req_all) {
		//每秒请求，新一轮请求，不满3秒就等等
		if (cnt_refresh_ == 0 && span < std::chrono::milliseconds(3000)) {
			return;
		}
	}
	else if (span < std::chrono::milliseconds(3000)) {
		return;
	}

	auto count_per_page = ctrl_list_->GetCountPerPage();
	auto top_row = req_all ? cnt_refresh_ *  count_per_page : ctrl_list_model_->GetRow(ctrl_list_->GetTopItem());
	if (top_row < 0 || top_row >= count) {
		top_row = 0;
	}
	std::vector<HZQDB> codes;
	uint32_t date = 0, time = 0;
	date = XUtil::NowDateTime(&time);
	for (auto i = 0; i < count_per_page; i++)
	{
		auto row = top_row + i;
		if (row >= count) {
			break;
		}
		SmartKBItem smkbi;
		if (ctrl_list_model_->GetResult(row, smkbi)) {
			HZQDB h = (HZQDB)smkbi.Data;
			if (!ZQDBIsSubscribeMarketDataAll(h)) {
				if (!ZQDBIsSubscribeMarketData(h)) {
					if (req_all || (first || cnt_refresh_ != top_row)) {
						codes.push_back(h);
					}
					else {
						auto cmp = ZQDBIsNotInTradingTime(h, date, time, nullptr);
						if (cmp <= 0) {
							codes.push_back(h);
						}
					}
				}
			}
		}
	}
	if (!codes.empty()) {
		std::array<size_t,6> ids = {
			MDB_FIELD_INDEX(ZQDB, CODE, OPEN),
			MDB_FIELD_INDEX(ZQDB, CODE, HIGH),
			MDB_FIELD_INDEX(ZQDB, CODE, LOW),
			MDB_FIELD_INDEX(ZQDB, CODE, CLOSE),
			MDB_FIELD_INDEX(ZQDB, CODE, VOLUME),
			MDB_FIELD_INDEX(ZQDB, CODE, AMOUNT)
		};
		ZQDBRequestMarketData(codes.data(), codes.size(), ids.data(), ids.size());
	}

	tp_refresh_ = now;
	if (req_all) {
		cnt_refresh_ += count_per_page;
		if (cnt_refresh_ >= count) {
			cnt_refresh_ = 0;
		}
	}
	else {
		cnt_refresh_ = top_row;
	}
}

wxBEGIN_EVENT_TABLE(MyCodeView, Base)
//EVT_ERASE_BACKGROUND(MyCodeView::OnErase)
//EVT_PAINT(MyCodeView::OnPaint)
EVT_TIMER(wxID_ANY, MyCodeView::OnTimer)
EVT_TEXT(wxID_EDIT, MyCodeView::OnSearch)
wxEND_EVENT_TABLE()

void MyCodeView::OnTimer(wxTimerEvent& event)
{
	if (IsTextSearching()) {
		return;
	}

	if (ctrl_list_->GetSortingColumn()) {
		ctrl_list_model_->Resort();
	}

	if (ctrl_list_model_->IsSort()) {
		ctrl_list_model_->Sort();
		Select(sel_, false);
	}
	else {
		ctrl_list_model_->UpdateResult();
	}
	DoRefresh();
}

void MyCodeView::OnSearch(wxCommandEvent& event)
{
	//wxBusyCursor busyCursor;
	if (pr_container_.second) {
		ctrl_list_model_->Search(this, pr_container_.second);
		ctrl_list_->SetFocus();
	}
	else {
		auto strKey = event.GetString().Trim().MakeUpper();
		if (strKey.IsEmpty()) {
			ctrl_list_model_->Search(this, wxEmptyString);
			//ctrl_text_->SetSelection(0, -1);
		} else {
			ctrl_list_model_->Search(this, strKey);
		} 
	}
}

void MyCodeView::OnSearchResult(wxCommandEvent& event)
{
	//wxBusyCursor busyCursor;
	bool text_searching = IsTextSearching();
	ctrl_list_model_->UpdateResult(true);
	if (!text_searching) {
		ctrl_list_model_->Sort();
	}
	if (ctrl_list_model_->GetCount()) {
		if (sel_) {
			Goto(sel_);
		}
		else {
			if (text_searching) {
				//DoSelect(0);
			}
			else {
				DoGoto(0);
			}
		}
		DoRefresh(true);
	}
	if (!ctrl_text_->HasFocus()) {
		if (!ctrl_list_->HasFocus()) {
			ctrl_list_->SetFocus();
		}
	}
}

void MyCodeView::Activate(const wxDataViewItem& item, bool sel)
{
	SmartKBItem smkbi;
	if (ctrl_list_model_->GetResult(item, smkbi)) {
		HZQDB h = (HZQDB)smkbi.Data;
		if (sel)
			sel_ = h;
		else
			sel_ = nullptr;
		MyTechFrame::GetFrameByChild(this)->Set(h);
	}
	else {
		sel_ = nullptr;
	}
	Refresh();
}

void MyCodeView::OnActivated(wxDataViewEvent &event)
{
	Activate(event.GetItem());
}

void MyCodeView::OnSelChanged(wxDataViewEvent &event)
{
	auto item = event.GetItem();
	Activate(item);
	if (item) {
		auto rcClient = ctrl_list_->GetClientRect();
		auto rcItem = ctrl_list_->GetItemRect(item);
		//只处理底部不可见
		if (rcItem.GetBottom() > rcClient.GetBottom()) {
			int scrollPos = 0;
			/*auto row = ctrl_list_model_->GetRow(item);
			auto count = ctrl_list_model_->GetCount();
			auto range = ctrl_list_->GetScrollRange(wxVERTICAL);
			auto pos = ctrl_list_->GetScrollPos(wxVERTICAL);
			pos = ((pos * count) / range + 1) * range / count;*/
			ctrl_list_->Scroll(0, scrollPos);
		}
	}
}

void MyCodeView::OnContextMenu(wxDataViewEvent &event)
{
	enum {
		ID_FUNC_ADD_SELFSEL = 1,
		ID_FUNC_REMOVE_SELFSEL,
		ID_FUNC_SUBSCRIBE,
		ID_FUNC_UNSUBSCRIBE,
		ID_FUNC_REMOVE,
		ID_FUNC_REFRESH,
	};
	wxMenu menu;
	HZQDB h = nullptr;
	size_t row = 0;
	auto sel = ctrl_list_->GetSelection();
	if (sel) {
		row = ctrl_list_model_->GetRow(sel);
		SmartKBItem smkbi;
		if (ctrl_list_model_->GetResult(row, smkbi)) {
			h = (HZQDB)smkbi.Data;
		}
		if (ZQDBGetFlags(h) & ZQDB_CODE_FLAG_SELFSEL) {
			menu.Append(ID_FUNC_REMOVE_SELFSEL, wxT("删除" SMARTKB_KEY_SELFSEL));
		}
		else {
			menu.Append(ID_FUNC_ADD_SELFSEL, wxT("添加" SMARTKB_KEY_SELFSEL));
		}
		//if (!ZQDBIsSubscribeMarketDataAll(h)) {
			if (ZQDBIsSubscribeMarketData(h)) {
				menu.Append(ID_FUNC_UNSUBSCRIBE, wxT("取消订阅"));
			}
			else {
				menu.Append(ID_FUNC_SUBSCRIBE, wxT("订阅"));
			}
		//}
		menu.AppendSeparator();
		menu.Append(ID_FUNC_REMOVE, wxT("从当前列表移除"));
	}
	menu.Append(ID_FUNC_REFRESH, wxT("刷新"));
	int id = ctrl_list_->GetPopupMenuSelectionFromUser(menu, event.GetPosition());
	if (id == wxID_NONE) {
		return;
	}
	switch (id)
	{
	case ID_FUNC_ADD_SELFSEL: {
		MyTechFrame::GetFrameByChild(this)->AddSelfSel(h);
		Refresh();
	} break;
	case ID_FUNC_REMOVE_SELFSEL: {
		MyTechFrame::GetFrameByChild(this)->RemoveSelfSel(h);
		bool need_redo = IsShowSearch(wxT(SMARTKB_KEY_SELFSEL));
		if (!need_redo) {
			if (!ZQDBIsSubscribeMarketData(h)) {
				if (IsShowSearch(wxT(SMARTKB_KEY_SUBSCRIBE))) {
					need_redo = true;
				}
			}
		}
		if (need_redo) {
			Redo();
		}
		Refresh();
	} break;
	case ID_FUNC_SUBSCRIBE: {
		MyTechFrame::GetFrameByChild(this)->Subscribe(h);
		Refresh();
	} break;
	case ID_FUNC_UNSUBSCRIBE: {
		MyTechFrame::GetFrameByChild(this)->UnSubscribe(h);
		if (IsShowSearch(wxT(SMARTKB_KEY_SUBSCRIBE))) {
			Redo();
		}
		Refresh();
	} break;
	case ID_FUNC_REMOVE: {
		ctrl_list_model_->RemoveResult(row);
		Refresh();
	} break;
	case ID_FUNC_REFRESH: {
		//Redo();
		Refresh();
	} break;
	default: {
	} break;
	}
}

///

MyUserView::MyUserView(wxWindow* parent, const char* xml, size_t xmlflag) : Base(parent, xml, xmlflag)
{
	btn_mmx_ = new wxButton(this, wxID_BTN_MMX, wxT("▲"), wxDefaultPosition, FromDIP(wxSize(30,-1)));
	//
	btn_add_user_ = new wxButton(this, wxID_BTN_ADD_USER, wxT("+"), wxDefaultPosition, FromDIP(wxSize(30, -1)));
	cmb_user_ = new wxComboBox(this, wxID_CMB_USER,
		wxEmptyString, wxDefaultPosition, FromDIP(wxSize(170, -1)), 0, nullptr, wxCB_READONLY);
	const std::array<wxString,6> price_type = {
		_("Market Price")
		, wxT("FAK")
		, wxT("FOK")
		, _("Limit Price")
		, _("Upper Price")
		, _("Lower Price")
	};
	cmb_price_type_ = new wxComboBox(this, wxID_CTRL_PRICE_TYPE,
		price_type[wxGetApp().GetTradePriceType()], wxDefaultPosition, FromDIP(wxSize(50, -1)), price_type.size(), price_type.data(), wxCB_READONLY);
	ctrl_price_ = new wxSpinCtrlDouble(this, wxID_CTRL_PRICE, wxEmptyString
		, wxDefaultPosition, FromDIP(wxSize(100, -1)), wxSP_ARROW_KEYS, -10000000.0, 10000000.0);
	ctrl_price_->SetDigits(2);
	const std::array<wxString,1> volume_type = {
		_("Number Volume")
		//, _("Number Amount")
	};
	cmb_volume_type_ = new wxComboBox(this, wxID_CTRL_VOLUME_TYPE, volume_type[0], wxDefaultPosition, FromDIP(wxSize(50, -1)), volume_type.size(), volume_type.data(), wxCB_READONLY);
	ctrl_volume_ = new wxSpinCtrlDouble(this, wxID_CTRL_VOLUME, wxT("1")
		, wxDefaultPosition, FromDIP(wxSize(80, -1)), wxSP_ARROW_KEYS, 0.0, 10000000.0);
	btn_quick_buy_ = new wxButton(this, wxID_BTN_QUICK_BUY, _("Quick Buy"), wxDefaultPosition, wxSize(60, -1));
	btn_quick_sell_close_ = new wxButton(this, wxID_BTN_QUICK_SELL_CLOSE, _("Quick Sell Close"), wxDefaultPosition, wxSize(60, -1));
#if wxUSE_STATLINE
	sl_buy_ = new wxStaticLine(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL);
#endif // wxUSE_STATLINE
	btn_quick_sell_ = new wxButton(this, wxID_BTN_QUICK_SELL, _("Quick Sell"), wxDefaultPosition, wxSize(60, -1));
	btn_quick_buy_close_ = new wxButton(this, wxID_BTN_QUICK_BUY_CLOSE, _("Quick Buy Close"), wxDefaultPosition, wxSize(60, -1));
	stc_info_ = new wxStaticText(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxALIGN_BOTTOM);
	
	//SetBackgroundStyle(wxBG_STYLE_CUSTOM);
	//SetBackgroundColour(btn_mmx_->GetBackgroundColour());

	wxSizer* topSizer = new wxBoxSizer(wxVERTICAL);

	wxSizer* sizer_user_h = new wxBoxSizer(wxHORIZONTAL);
	//sizer_user_h->Add(btn_mmx_, 0, wxEXPAND);
	sizer_user_h->Add(btn_add_user_, 0, wxEXPAND);
	sizer_user_h->Add(cmb_user_, 0, wxUP | wxDOWN, 1);

	sizer_user_h->AddSpacer(5);
	sizer_user_h->Add(cmb_price_type_, 0, wxEXPAND | wxUP | wxDOWN, 1);
	sizer_user_h->Add(ctrl_price_, 0, wxEXPAND | wxUP | wxDOWN, 1);
	sizer_user_h->AddSpacer(5);
	sizer_user_h->Add(cmb_volume_type_, 0, wxUP | wxDOWN, 1);
	sizer_user_h->Add(ctrl_volume_, 0, wxUP | wxDOWN, 1);
	sizer_user_h->AddSpacer(5);
	sizer_user_h->Add(btn_quick_buy_, 0, wxEXPAND);
	sizer_user_h->AddSpacer(5);
	sizer_user_h->Add(btn_quick_sell_close_, 0, wxEXPAND);
	sizer_user_h->AddSpacer(5);
#if wxUSE_STATLINE
	sizer_user_h->Add(sl_buy_, 0, wxEXPAND | wxUP | wxDOWN, 2);
#endif // wxUSE_STATLINE
	sizer_user_h->AddSpacer(5);
	sizer_user_h->Add(btn_quick_sell_, 0, wxEXPAND);
	sizer_user_h->AddSpacer(5);
	sizer_user_h->Add(btn_quick_buy_close_, 0, wxEXPAND);
	sizer_user_h->AddSpacer(5);

	sizer_user_h->AddStretchSpacer();
	/*wxSizer* sizer_stc_info = new wxBoxSizer(wxVERTICAL);
	sizer_stc_info->AddStretchSpacer();
	sizer_stc_info->Add(stc_info_, 0, wxEXPAND);
	sizer_stc_info->AddStretchSpacer();*/
	sizer_user_h->Add(stc_info_, 0, wxALIGN_CENTER_VERTICAL);
	
	sizer_user_h->AddSpacer(5);
	sizer_user_h->Add(btn_mmx_, 0, wxEXPAND);

	topSizer->Add(sizer_user_h, 0, wxEXPAND);

	DoUpdatePriceType();

	SetSizer(topSizer);

	DoUpdateAll();
}

MyUserView::~MyUserView()
{

}

void MyUserView::Goto(HZQDB user)
{
	DoSelect(user);
}

void MyUserView::OnSkinInfoChanged()
{
	Base::OnSkinInfoChanged();

	auto skin_info_ptr = std::static_pointer_cast<SkinInfo>(skin_info_ptr_);
	SetBackgroundColour(skin_info_ptr->crPrimary);
	SetForegroundColour(skin_info_ptr->crTertiary);
	btn_mmx_->SetBackgroundColour(skin_info_ptr->crPrimary);
	btn_mmx_->SetForegroundColour(skin_info_ptr_->GetArtColor(wxRIBBON_ART_TAB_LABEL_COLOUR));
	btn_add_user_->SetBackgroundColour(skin_info_ptr->crPrimary);
	btn_add_user_->SetForegroundColour(skin_info_ptr_->GetArtColor(wxRIBBON_ART_TAB_LABEL_COLOUR));
	//cmb_user_->SetBackgroundColour(skin_info_ptr->crPrimary);
	//cmb_user_->SetForegroundColour(skin_info_ptr->crCtrlText); 
	//cmb_price_type_->SetBackgroundColour(skin_info_ptr->crPrimary);
	//cmb_price_type_->SetForegroundColour(skin_info_ptr_->GetArtColor(wxRIBBON_ART_TAB_LABEL_COLOUR));
	//ctrl_price_->SetBackgroundColour(skin_info_ptr->crPrimary);
	//cmb_volume_type_->SetBackgroundColour(skin_info_ptr->crPrimary);
	//cmb_volume_type_->SetForegroundColour(skin_info_ptr_->GetArtColor(wxRIBBON_ART_TAB_LABEL_COLOUR));
	//ctrl_volume_->SetBackgroundColour(skin_info_ptr->crPrimary);
	btn_quick_buy_->SetBackgroundColour(skin_info_ptr->crPrimary);
	btn_quick_buy_->SetForegroundColour(skin_info_ptr->crCtrlRising);
	btn_quick_sell_->SetBackgroundColour(skin_info_ptr->crPrimary);
	btn_quick_sell_->SetForegroundColour(skin_info_ptr->crCtrlFalling);
	btn_quick_buy_close_->SetBackgroundColour(skin_info_ptr->crPrimary);
	btn_quick_buy_close_->SetForegroundColour(skin_info_ptr->crCtrlRising);
	btn_quick_sell_close_->SetBackgroundColour(skin_info_ptr->crPrimary);
	btn_quick_sell_close_->SetForegroundColour(skin_info_ptr->crCtrlFalling);
	//btn_cancel_all_->SetBackgroundColour(skin_info_ptr->crPrimary);
	//btn_cancel_all_->SetForegroundColour(skin_info_ptr_->GetArtColor(wxRIBBON_ART_TAB_LABEL_COLOUR));
	//btn_close_all_->SetBackgroundColour(skin_info_ptr->crPrimary);
	//btn_close_all_->SetForegroundColour(skin_info_ptr_->GetArtColor(wxRIBBON_ART_TAB_LABEL_COLOUR));
	stc_info_->SetForegroundColour(skin_info_ptr_->GetArtColor(wxRIBBON_ART_TAB_LABEL_COLOUR));
	if (user_view_) {
		user_view_->SetSkinInfo(skin_info_ptr_);
	}
}

void MyUserView::OnModuleChanged()
{
	Base::OnModuleChanged();
	if (module_ != user_module_) {
		size_t user_count = cmb_user_->GetCount();
		if (user_count) {
			for (size_t i = 0; i < user_count; i++)
			{
				auto user = (HZQDB)cmb_user_->GetClientData(i);
				if (module_ == wxGetApp().FindModule(user)) {
					DoSelect(user);
				}
			}
		}
	}
}

void MyUserView::OnHandleChanged()
{
	Base::OnHandleChanged();
	zqdb::Code code(h_);
	zqdb::Product product(code.GetProduct());
	DoUpatePrice(true);
	DoUpateVolume(true);
	bool enable = product->Type != PRODUCT_TYPE_Index;
	btn_quick_buy_->Enable(enable);
	btn_quick_sell_close_->Enable(enable);
	sl_buy_->Enable(enable);
	btn_quick_sell_->Enable(enable);
	btn_quick_buy_close_->Enable(enable);
	if (product->Type == PRODUCT_TYPE_Index) {
		//
	} else if (product->Type != PRODUCT_TYPE_Futures) {
		btn_quick_buy_->Show(true);
		btn_quick_sell_close_->Show(true);
		sl_buy_->Show(false);
		btn_quick_sell_->Show(false);
		btn_quick_buy_close_->Show(false);
		btn_quick_buy_->SetLabel(wxT("买入"));
		btn_quick_sell_close_->SetLabel(wxT("卖出"));
	}
	else {
		btn_quick_buy_->Show(true);
		btn_quick_sell_close_->Show(true);
		sl_buy_->Show(true);
		btn_quick_sell_->Show(true);
		btn_quick_buy_close_->Show(true);
		btn_quick_buy_->SetLabel(wxT("开多"));
		btn_quick_sell_close_->SetLabel(wxT("平多"));
		btn_quick_sell_->SetLabel(wxT("开空"));
		btn_quick_buy_close_->SetLabel(wxT("平空"));
	}
	if (user_view_) {
		user_view_->SetHandle(h_);
	}
}

void MyUserView::OnUserModuleChanged()
{

}

void MyUserView::OnUserChanged()
{
	UserBase::OnUserChanged();

	DoUpateVolume(true);

	if (user_module_) {
		stc_info_->SetLabel(user_module_->GetUserInfo(user_));
	}
	else {
		stc_info_->SetLabel(wxEmptyString);
	}

	if (user_view_) {
		user_view_->SetUser(user_);
	}

	if (mmx_) {
		if (user_)
			DoShow();
		else
			DoHide();
	}
}

void MyUserView::OnStartTest()
{
	DoUpdateAll();
	DoSelect(GetUser());
}

void MyUserView::OnStopTest()
{
	DoUpdateAll();
	DoSelect(GetUser());
}

void MyUserView::OnNotifyStatus(HZQDB h)
{
	if (h->type == ZQDB_HANDLE_TYPE_MODULE) {
		DoUpdateAll();
		DoSelect(GetUser());
	}
	if (user_view_) {
		user_view_->OnNotifyStatus(h);
	}
}

void MyUserView::OnNotifyAdd(HZQDB h) 
{
	switch (h->type)
	{
	case ZQDB_HANDLE_TYPE_USER: {
		DoUpdateAll();
		if (module_ == wxGetApp().FindModule(h)) {
			DoSelect(h);
			if (!mmx_) {
				DoShow();
			}
		}
		else {
			DoSelect(GetUser());
		}
		return;
	} break;
	default: {
		//
	} break;
	}
	if (user_view_) {
		user_view_->OnNotifyAdd(h);
	}
}

void MyUserView::OnNotifyRemove(HZQDB h) 
{
	if (user_view_) {
		user_view_->OnNotifyRemove(h);
	}
}

void MyUserView::OnNotifyUpdate(HZQDB h)
{
	bool relayout = false;
	if (user_module_) {
		bool update_user_info = false;
		if (h == user_) {
			update_user_info = true;
		}
		else if (ZQDBGetModule(h) == *user_module_) {
			if (ZQDBIsDataOfTable(h, STR_ZQDB_TABLE_ACCOUNT)) {
				update_user_info = true;
			}
		}
		if (update_user_info) {
			auto info = user_module_->GetUserInfo(user_);
			if (info.Length() != stc_info_->GetLabel().Length()) {
				relayout = true;
			}
			stc_info_->SetLabel(info);
		}
	}
	if (h == h_) {
		DoUpatePrice();
	}
	if (user_view_) {
		user_view_->OnNotifyUpdate(h);
	}
	if (relayout) {
		this->Layout();
	}
}

void MyUserView::DoUpdateAll()
{
	cmb_user_->Clear();
	if (ZQDBIsTest()) {
		size_t user_count = ZQDBGetCalcUserCount();
		if (!user_count) {
			//
		}
		else {
			for (size_t i = 0; i < user_count; i++)
			{
				auto h = ZQDBGetCalcUser(i);
				zqdb::ObjectT<tagUserInfo> user(h);
				zqdb::ObjectT<tagZQDBModuleInfo> module(ZQDBGetModule(h));
				wxString strUser = utf2wxString(user->User);
				strUser += wxT(".");
				if (strcmp(user->Broker, ZQDB_USER_BROKER_MOCK) == 0) {
					continue;
				}
				else if (strcmp(user->Broker, ZQDB_USER_BROKER_TEST) == 0) {
					if (stricmp(module->Code, "ctp") == 0) {
						strUser += wxT("期货测试");
					}
					else if (stricmp(module->Code, "tora") == 0) {
						strUser += wxT("股票测试");
					}
					else {
						strUser += utf2wxString(module->Name);
						strUser += wxT("测试");
					}
				}
				else {
					continue;
				}
				cmb_user_->Append(strUser, (void*)h);
			}
		}
	}
	else {
		if (IsTest()) {
			for (auto h : test_users_)
			{
				zqdb::ObjectT<tagUserInfo> user(h);
				zqdb::ObjectT<tagZQDBModuleInfo> module(ZQDBGetModule(h));
				wxString strUser = utf2wxString(user->User);
				strUser += wxT(".");
				if (strcmp(user->Broker, ZQDB_USER_BROKER_MOCK) == 0) {
					wxASSERT(0);
				}
				else if (strcmp(user->Broker, ZQDB_USER_BROKER_TEST) == 0) {
					if (stricmp(module->Code, "ctp") == 0) {
						strUser += wxT("期货回测");
					}
					else if (stricmp(module->Code, "tora") == 0) {
						strUser += wxT("股票回测");
					}
					else {
						strUser += utf2wxString(module->Name);
						strUser += wxT("回测");
					}
				}
				else {
					wxASSERT(0);
				}
				cmb_user_->Append(strUser, (void*)h);
			}
		}
		else {
			size_t user_count = ZQDBGetCalcUserCount();
			if (!user_count) {
				//
			}
			else {
				for (size_t i = 0; i < user_count; i++)
				{
					auto h = ZQDBGetCalcUser(i);
					zqdb::ObjectT<tagUserInfo> user(h);
					zqdb::ObjectT<tagZQDBModuleInfo> module(ZQDBGetModule(h));
					wxString strUser = utf2wxString(user->User);
					strUser += wxT(".");
					if (strcmp(user->Broker, ZQDB_USER_BROKER_MOCK) == 0) {
						if (IsTest()) {
							continue;
						}
						if (stricmp(module->Code, "ctp") == 0) {
							strUser += wxT("期货模拟");
						}
						else if (stricmp(module->Code, "tora") == 0) {
							strUser += wxT("股票模拟");
						}
						else {
							strUser += utf2wxString(module->Name);
							strUser += wxT("模拟");
						}
					}
					else if (strcmp(user->Broker, ZQDB_USER_BROKER_TEST) == 0) {
						if (!IsTest()) {
							continue;
						}
						if (stricmp(module->Code, "ctp") == 0) {
							strUser += wxT("期货回测");
						}
						else if (stricmp(module->Code, "tora") == 0) {
							strUser += wxT("股票回测");
						}
						else {
							strUser += utf2wxString(module->Name);
							strUser += wxT("回测");
						}
					}
					else {
						if (IsTest()) {
							continue;
						}
						strUser += utf2wxString(module->Name);
					}
					cmb_user_->Append(strUser, (void*)h);
				}
			}
		}
	}
}

void MyUserView::DoSelect(HZQDB user)
{
	size_t user_count = cmb_user_->GetCount();
	if (user_count <= 0) {
		DoGoto(nullptr);
	}
	else {
		int sel = -1;
		if (user) {
			for (size_t i = 0; i < user_count; i++)
			{
				if (cmb_user_->GetClientData(i) == user) {
					sel = i;
					break;
				}
			}
		}
		if (sel < 0) {
			sel = 0;
			user = (HZQDB)cmb_user_->GetClientData(sel);
		}
		cmb_user_->Select(sel);
		DoGoto(user);
	}
}

void MyUserView::DoGoto(HZQDB user)
{
	if (user == user_) {
		return;
	}

	Freeze();

	if (!user || (user_module_ && *user_module_ != ZQDBGetModule(user))) {
		if (mmx_) {
			auto sizer = GetSizer();
			if (user_view_) {
				sizer->Remove(1);
				delete user_view_;
				user_view_ = nullptr;
			}
		}
	}

	MyTechFrame::GetFrameByChild(this)->SetUser(user);

	Layout();
	Thaw();
}

void MyUserView::DoShow()
{
	if (user_view_) {
		return;
	}
	if (user_module_) {
		//显示user模块视图
		user_view_ = std::static_pointer_cast<MyModule>(user_module_)->NewMiniView(this);
	}
	if (!user_view_) {
		return;
	}

	mmx_ = true;
	btn_mmx_->SetLabel(wxT("▼"));
	auto sizer = GetSizer();
	user_view_->SetSkinInfo(GetSkinInfo());
	user_view_->SetHandle(h_);
	user_view_->SetUser(user_);
	sizer->Add(user_view_, 1, wxEXPAND);
	GetParent()->Layout();
}

void MyUserView::DoHide()
{
	auto sizer = GetSizer();
	if (sizer->GetItemCount() < 2) {
		ASSERT(!user_view_);
		return;
	}
	ASSERT(user_view_);

	mmx_ = false;
	btn_mmx_->SetLabel(wxT("▲"));
	sizer->Remove(1);
	delete user_view_;
	user_view_ = nullptr;
	GetParent()->Layout();
}

void MyUserView::DoUpatePrice(bool set)
{
	char type;
	double price;
	std::tie(type, price) = GetPrice(set);
	if (set) {
		zqdb::Code code(h_);
		/*if (code->Upper > code->Lower && !ZQDBIsInvalidValue(code->Upper)) {
			ctrl_price_->SetRange(code->Lower, code->Upper);
		}
		else {
			ctrl_price_->SetRange(std::numeric_limits<double>::lowest(), std::numeric_limits<double>::infinity());
		}*/
		ctrl_price_->SetIncrement(code->PriceTick);
		ctrl_price_->SetValue(price);
	}
	else {
		switch (type)
		{
		case ORDER_LIMIT: {
		} break;
		case ORDER_MARKET:
		case ORDER_FAK:
		case ORDER_FOK: {
			ctrl_price_->SetValue(price);
		} break;
		}
	}
}

void MyUserView::DoUpateVolume(bool set)
{
	char type;
	double volume;
	std::tie(type, volume) = GetVolume();
	if (set) {
		zqdb::ObjectT<tagProductInfo> product(ZQDBGetParent(h_));
		if (product) {
			auto unit = product->TradingUnit;
			ctrl_volume_->SetRange(unit, ctrl_volume_->GetMax());
			ctrl_volume_->SetIncrement(unit);
			ctrl_volume_->SetValue(user_module_ ? user_module_->OrderDefaultVolume(user_, h_) : 0);
		}
	}
	else {
		//
	}
}

void MyUserView::Draw(wxDC& dc)
{
}

wxBEGIN_EVENT_TABLE(MyUserView, Base)
//EVT_ERASE_BACKGROUND(MyUserView::OnErase)
//EVT_PAINT(MyUserView::OnPaint)
EVT_TIMER(wxID_ANY, MyUserView::OnTimer)
EVT_BUTTON(wxID_BTN_MMX, MyUserView::OnBtnMMX)
EVT_BUTTON(wxID_BTN_ADD_USER, MyUserView::OnBtnAddUser)
EVT_COMBOBOX(wxID_CMB_USER, MyUserView::OnCmbUserUpdate)
EVT_COMBOBOX(wxID_CTRL_PRICE_TYPE, MyUserView::OnCmbPriceTypeUpdate)
EVT_COMBOBOX(wxID_CTRL_VOLUME_TYPE, MyUserView::OnCmbVolumeTypeUpdate)
EVT_BUTTON(wxID_BTN_QUICK_BUY, MyUserView::OnBtnQuickBuy)
EVT_BUTTON(wxID_BTN_QUICK_SELL, MyUserView::OnBtnQuickSell)
EVT_BUTTON(wxID_BTN_QUICK_BUY_CLOSE, MyUserView::OnBtnQuickBuyClose)
EVT_BUTTON(wxID_BTN_QUICK_SELL_CLOSE, MyUserView::OnBtnQuickSellClose)
EVT_BUTTON(wxID_BTN_CANCEL_ALL, MyUserView::OnBtnCancelAll)
EVT_BUTTON(wxID_BTN_CLOSE_ALL, MyUserView::OnBtnCloseAll)
wxEND_EVENT_TABLE()

void MyUserView::OnErase(wxEraseEvent &event)
{

}

void MyUserView::OnPaint(wxPaintEvent &event)
{
	wxPaintDC dc(this);
	dc.Clear();
	if (IsDispOk()) {
		Draw(dc);
	}
}

void MyUserView::OnTimer(wxTimerEvent& event)
{
	if (user_view_) {
		user_view_->GetEventHandler()->ProcessEvent(event);
	}
}

void MyUserView::OnBtnMMX(wxCommandEvent& event)
{
	if (mmx_) {
		//收起
		DoHide();
	} else {
		//展开
		DoShow();
	}
}

void MyUserView::OnBtnAddUser(wxCommandEvent& event)
{
	MyLoginDlg dlg(MyTechFrame::GetFrameByChild(this));
	dlg.ShowModal();
	/*lock_ = !lock_;
	btn_lock_->SetLabel(lock_ ? wxT("显") : wxT("隐"));
	if (mmx_) {
		//收起
		DoHide();
	}
	bool show = !lock_;
	btn_mmx_->Show(show);
	cmb_user_->Show(show);
	cmb_price_type_->Show(show);
	ctrl_price_->Show(show);
	cmb_volume_type_->Show(show);
	ctrl_volume_->Show(show);
	btn_quick_buy_->Show(show);
	btn_quick_sell_->Show(show);
	btn_quick_buy_close_->Show(show);
	btn_quick_sell_close_->Show(show);
	//btn_cancel_all_->Show(show);
	//btn_close_all_->Show(show);
	stc_info_->Show(show);*/
}

void MyUserView::OnCmbUserUpdate(wxCommandEvent& event)
{
	auto user = (HZQDB)cmb_user_->GetClientData(event.GetSelection());
	if (user_ != user) {
		DoGoto(user);
	}
}

std::tuple<char, double> MyUserView::GetPrice(bool set)
{
	char type = ORDER_LIMIT;
	double price = 0;
	auto sel = cmb_price_type_->GetSelection();
	if (set) {
		wxGetApp().SetTradePriceType(sel);
	}
	switch (sel)
	{
	case TRADE_PRICE_Market: {//Market Price
		type = ORDER_MARKET;
	} break;
	case TRADE_PRICE_FAK: {//FAK
		type = ORDER_FAK;
	} break;
	case TRADE_PRICE_FOK: {//FOK
		type = ORDER_FOK;
	} break;
	}
	if (h_) {
		zqdb::Code code(h_);
		switch (sel)
		{
		case TRADE_PRICE_Market: {//Market Price
			price = code->Close;
		} break;
		case TRADE_PRICE_FAK: {//FAK
			price = code->Close;
		} break;
		case TRADE_PRICE_FOK: {//FOK
			price = code->Close;
		} break;
		case TRADE_PRICE_Limit: {//Limit Price
			price = set ? code->Close : ctrl_price_->GetValue();
		} break;
		case TRADE_PRICE_Upper: {//Upper Price
			price = set ? code->Upper : ctrl_price_->GetValue();
		} break;
		case TRADE_PRICE_Lower: {//Lower Price
			price = set ? code->Lower : ctrl_price_->GetValue();
		} break;
		}
	}
	return std::make_tuple(type, price);
}

std::tuple<char, double> MyUserView::GetVolume()
{
	auto type = cmb_volume_type_->GetSelection();
	double volume = 0.;
	switch (type)
	{
	case 0: {//Number Volume
		volume = ctrl_volume_->GetValue();
	} break;
	case 1: {//Number Amount
		volume = ctrl_volume_->GetValue();
	} break;
	}
	return std::make_tuple(type, volume);
}

void MyUserView::DoUpdatePriceType()
{
	char type;
	double price;
	std::tie(type, price) = GetPrice(true);
	ctrl_price_->SetValue(price);
	switch (type)
	{
	case ORDER_LIMIT: {
		ctrl_price_->Enable(true);
	} break;
	case ORDER_MARKET:
	case ORDER_FAK:
	case ORDER_FOK: {
		ctrl_price_->Enable(false);
	} break;
	}
}

void MyUserView::OnCmbPriceTypeUpdate(wxCommandEvent& event)
{
	DoUpdatePriceType();
}

void MyUserView::OnCmbVolumeTypeUpdate(wxCommandEvent& event)
{
	
}

bool MyUserView::CheckParams()
{
	if (!h_) {
		wxMessageBox(wxT("当前代码为空!!!"), wxT("提示"));
		return false;
	}
	if (!user_) {
		wxMessageBox(wxT("当前用户为空!!!"), wxT("提示"));
		return false;
	}
	if (!user_module_) {
		wxMessageBox(wxT("当前用户不支持交易!!!"), wxT("提示"));
		return false;
	}
	return true;
}

void MyUserView::OrderSend(char direction)
{
	if (!CheckParams()) {
		return;
	}
	char type;
	double price;
	std::tie(type, price) = GetPrice();
	char volume_type;
	double volume;
	std::tie(volume_type, volume) = GetVolume();
#if 1
	wxGetApp().SendOrder(user_, h_, direction, OFFSET_OPEN, type, volume, price);
#else
	wxString strTips;
	HNMSG rsp = nullptr;
	int err = user_module_->OrderSend(user_, h_, direction, OFFSET_OPEN, type, volume, price, &rsp);
	if (rsp) {
		zqdb::Msg rsp_msg(rsp, zqdb::Msg::AutoDelete);
		auto errorcode = rsp_msg.GetParamAsInt(STR_ZQDB_MSG_ERROR_CODE, 0);
		auto errormsg = utf2wxString(rsp_msg.GetParam(STR_ZQDB_MSG_ERROR_MESSAGE, ""));
		if (!errorcode) {
			strTips = wxString::Format(wxT("提交订单成功。\n")
				wxT("订单ID：%s"), rsp_msg.GetParam(STR_MDB_FIELD_INDEX(ZQDB, ORDER, ORDER), ""));
		}
		else {
			strTips = wxString::Format(wxT("提交订单失败!!!\n")
				wxT("错误原因：[%zd]%s"), errorcode, errormsg);
		}
	}
	else {
		strTips = wxString::Format(wxT("提交订单失败!!!"));
	}
	wxMessageBox(strTips, wxT("提示"));
#endif
}

void MyUserView::OnBtnQuickBuy(wxCommandEvent& event)
{
	OrderSend(DIRECTION_LONG);
}

void MyUserView::OnBtnQuickSell(wxCommandEvent& event)
{
	OrderSend(DIRECTION_SHORT);
}

void MyUserView::OrderClose(char direction)
{
	if (!CheckParams()) {
		return;
	}
	char type;
	double price;
	std::tie(type, price) = GetPrice();
	char volume_type;
	double volume;
	std::tie(volume_type, volume) = GetVolume();
	wxString strTips;
	zqdb::AllTableData allposition(STR_ZQDB_TABLE_POSITION, *user_module_);
	for (auto h : allposition)
	{
		zqdb::ObjectT<tagPositionInfo> position(h);
		zqdb::Code code(h_);
		if (position->Direction == direction && strcmp(code->TradeCode, position->Code) == 0 && strcmp(code->Exchange, position->Exchange) == 0) {
			if (!strTips.IsEmpty()) {
				strTips += wxT("\n");
			}
			HNMSG rsp = nullptr;
			int err = user_module_->OrderClose(user_, h, type, volume, price, &rsp);
			if (rsp) {
				zqdb::Msg rsp_msg(rsp, zqdb::Msg::AutoDelete);
				auto errorcode = rsp_msg.GetErrorCode();
				auto errormsg = utf2wxString(rsp_msg.GetErrorMsg());
				if (!errorcode) {
					strTips += wxString::Format(wxT("提交订单成功。\n")
						wxT("订单ID：%s"), rsp_msg.GetParam(STR_MDB_FIELD_INDEX(ZQDB, ORDER, ORDER), ""));
				}
				else {
					strTips += wxString::Format(wxT("提交订单失败!!!\n")
						wxT("错误原因：[%zd]%s"), errorcode, errormsg);
				}
			}
			else {
				strTips += wxString::Format(wxT("提交订单失败!!!"));
			}
			break;
		}
	}
	if (strTips.IsEmpty()) {
		wxMessageBox(wxT("无仓可平!!!"), wxT("提示"));
	}
	else {
		wxMessageBox(strTips, wxT("提示"));
	}
}

void MyUserView::OnBtnQuickBuyClose(wxCommandEvent& event)
{
	OrderClose(DIRECTION_SHORT);
}

void MyUserView::OnBtnQuickSellClose(wxCommandEvent& event)
{
	OrderClose(DIRECTION_LONG);
}

void MyUserView::OnBtnCancelAll(wxCommandEvent& event)
{
	if (!CheckParams()) {
		return;
	}
	wxString strTips;
	zqdb::AllTableData allorder(STR_ZQDB_TABLE_ORDER, *user_module_);
	for (auto h : allorder)
	{
		zqdb::ObjectT<tagOrderInfo> order(h);
		if (ZQDBOrderIsFinal(order->Status)) {
			continue;
		}
		zqdb::Code code(h_);
		if (!strTips.IsEmpty()) {
			strTips += wxT("\n");
		}
		HNMSG rsp = nullptr;
		int err = user_module_->OrderCancel(user_, h, &rsp, 3000);
		if (rsp) {
			zqdb::Msg rsp_msg(rsp, zqdb::Msg::AutoDelete);
			auto errorcode = rsp_msg.GetErrorCode();
			auto errormsg = utf2wxString(rsp_msg.GetErrorMsg());
			if (!errorcode) {
				strTips += wxString::Format(wxT("订单%s撤销成功。"), order->Order);
			}
			else {
				strTips += wxString::Format(wxT("订单%s撤销失败，错误原因：[%zd]%s"), order->Order, errorcode, errormsg);
			}
		}
		else {
			strTips += wxString::Format(wxT("提交撤单失败!!!"));
		}
	}
	if (strTips.IsEmpty()) {
		wxMessageBox(wxT("无单可撤!!!"), wxT("提示"));
	}
	else {
		wxMessageBox(strTips, wxT("提示"));
	}
}

void MyUserView::OnBtnCloseAll(wxCommandEvent& event)
{
	if (!CheckParams()) {
		return;
	}
	wxString strTips;
	zqdb::AllTableData allposition(STR_ZQDB_TABLE_POSITION, *user_module_);
	for (auto h : allposition)
	{
		if (!strTips.IsEmpty()) {
			strTips += wxT("\n");
		}
		zqdb::ObjectT<tagPositionInfo> position(h);
		zqdb::Code code(h_);
		wxString strTips;
		HNMSG rsp = nullptr;
		int err = user_module_->OrderClose(user_, h, ORDER_MARKET, position->Volume - position->FrozenVolume, 0, &rsp);
		if (rsp) {
			zqdb::Msg rsp_msg(rsp, zqdb::Msg::AutoDelete);
			auto errorcode = rsp_msg.GetErrorCode();
			auto errormsg = utf2wxString(rsp_msg.GetErrorMsg());
			if (!errorcode) {
				strTips += wxString::Format(wxT("提交订单成功。\n")
					wxT("订单ID：%s"), rsp_msg.GetParam(STR_MDB_FIELD_INDEX(ZQDB, ORDER, ORDER), ""));
			}
			else {
				strTips += wxString::Format(wxT("提交订单失败!!!\n")
					wxT("错误原因：[%zd]%s"), errorcode, errormsg);
			}
		}
		else {
			strTips += wxString::Format(wxT("提交订单失败!!!"));
		}
	}
	if (strTips.IsEmpty()) {
		wxMessageBox(wxT("无仓可平!!!"), wxT("提示"));
	}
	else {
		wxMessageBox(strTips, wxT("提示"));
	}
}

///

#include "green.xpm"
#include "red.xpm"

//static const char *numlockIndicators[] = { "OFF", "NUM" };
//static const char *capslockIndicators[] = { "", "CAPS" };

LRESULT MyStatusBar::MyWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	auto pThis = (This*)GetProp(hwnd, _T("MyStatusBar"));
	switch (uMsg)
	{
	case WM_NCHITTEST: {
		return HTTRANSPARENT;
	} break;
	}
	return CallWindowProc(pThis->old_wndproc_, hwnd, uMsg, wParam, lParam);
}

MyStatusBar::MyStatusBar(wxWindow *parent, const char* xml, size_t xmlflag) : Base(parent, xml, xmlflag)
{
	// compute the size needed for num lock indicator pane
	/*wxClientDC dc(this);
	wxSize sizeNumLock = dc.GetTextExtent(numlockIndicators[0]);
	sizeNumLock.IncTo(dc.GetTextExtent(numlockIndicators[1]));

	int widths[Field_Max];
	widths[Field_Text] = -1; // growable
	widths[Field_Checkbox] = 150;
	widths[Field_Bitmap] = BITMAP_SIZE_X;
	widths[Field_NumLockIndicator] = sizeNumLock.x;
	widths[Field_Clock] = 100;
	widths[Field_CapsLockIndicator] = dc.GetTextExtent(capslockIndicators[1]).x;

	SetFieldsCount(Field_Max);
	SetStatusWidths(Field_Max, widths);*/

	//search_ctrl_ = new wxSearchCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition,
	//	FromDIP(wxSize(200, -1)), wxNO_BORDER);

	bmpStatus_[0] = std::move(wxBitmap(green_xpm));
	bmpStatus_[1] = std::move(wxBitmap(red_xpm));

	//SetMinHeight(wxMax(m_statbmp->GetBestSize().GetHeight(),
	//	m_checkbox->GetBestSize().GetHeight()));

	//SetDoubleBuffered(true);

	/*HWND hwnd = wxStatusBar::GetHandle();
	::SetProp(hwnd, _T("MyStatusBar"), (HANDLE)this);
	old_wndproc_ = (WNDPROC)SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)MyWndProc);*/
}

MyStatusBar::~MyStatusBar()
{
}

void MyStatusBar::ShowTips()
{
	tips_value_ = wxGetApp().GetLastTips(&tips_level_);
	tips_highlight_ = true;
	SetStatusText(tips_value_);
}

void MyStatusBar::OnSkinInfoChanged()
{
	Base::OnSkinInfoChanged();
	auto skin_info_ptr = std::static_pointer_cast<SkinInfo>(skin_info_ptr_);
	SetFont(skin_info_ptr->artProvider->GetFont(wxRIBBON_ART_TAB_LABEL_FONT));
	SetBackgroundColour(skin_info_ptr->crPrimary);
	SetForegroundColour(skin_info_ptr->artProvider->GetColor(wxRIBBON_ART_TAB_LABEL_COLOUR));

	tips_bmp_[ZQDB_LOG_LEVEL_ERROR] = skin_info_ptr->GetBitmap16(wxT("error"));
	tips_bmp_[ZQDB_LOG_LEVEL_WARN] = skin_info_ptr->GetBitmap16(wxT("warn"));
	tips_bmp_[ZQDB_LOG_LEVEL_INFO] = skin_info_ptr->GetBitmap16(wxT("info"));
	tips_bmp_[ZQDB_LOG_LEVEL_DEBUG] = skin_info_ptr->GetBitmap16(wxT("debug"));
	
	fontIndex_ = skin_info_ptr_->artProvider->GetFont(wxRIBBON_ART_PANEL_LABEL_FONT);

	Reset();
}

bool MyStatusBar::UpdateStatus()
{
	int nNewStatus = 0;
	for (auto h : all_status_)
	{
		zqdb::Status status(h);
		if (status->Status != ZQDB_STATUS_LOGGED) {
			nNewStatus = 1;
			break;
		}
	}
	if (nStatus_ != nNewStatus) {
		nStatus_ = nNewStatus;
		return true;
	}
	return false;
}

void MyStatusBar::OnHandleChanged()
{

}

void MyStatusBar::OnUserChanged()
{

}

void MyStatusBar::OnNotifyStatus(HZQDB h)
{
	if (h->type == ZQDB_HANDLE_TYPE_MODULE) {
		if (ZQDBIsDisable(h))
			;
		else
			Reset();
	}
}

void MyStatusBar::OnNotifyAdd(HZQDB h)
{
	if (h->type == ZQDB_HANDLE_TYPE_STATUS) {
		all_status_.Update();
		if (UpdateStatus()) {
			Update();
		}
	}
}

void MyStatusBar::OnNotifyRemove(HZQDB h)
{

}

void MyStatusBar::OnNotifyUpdate(HZQDB h)
{
	if (h->type == ZQDB_HANDLE_TYPE_STATUS) {
		if (UpdateStatus()) {
			Update();
		}
	}
}

void MyStatusBar::Reset()
{
	std::vector<int> widths = { -1 };

	//auto font = GetFont();
	wxClientDC dc(this);
	/*wxSize szExchange = dc.GetTextExtent(wxT("CFFEX 14:45:06"));

	allexchange_.Update();
	for (size_t i = 0; i < allexchange_.size(); i++)
	{
		//zqdb::Exchange exchange(allexchange_[i]);
		//wxSize szExchange = dc.GetTextExtent(wxString::Format(wxT("%s 14:45:06"), exchange->Exchange));
		widths.emplace_back(szExchange.x);
	}*/

	//指数
	indexs_.clear();
	wxSize szIndex;
	wxString strIndexBar;
	bool hsj = false;
	for (size_t i = 0, j = ZQDBGetModuleCount(); i < j; i++)
	{
		zqdb::ObjectT<tagZQDBModuleInfo> module(ZQDBGetModuleAt(i));
		if (module->flags & ZQDB_MODULE_FLAG_EXCHANGE_HSJ) {
			hsj = true;
			break;
		}
	}
	if (hsj) {
		szIndex = dc.GetTextExtent(wxT("上证指数   2946.34  +60.04  +2.09%"));
		strIndexBar = wxT("000001.SSE,399001.SZSE,399006.SZSE,000688.SSE");
	}
	else {
		szIndex = dc.GetTextExtent(wxT("中证500指数主力   5393.34  +60.04  +2.09%"));
		strIndexBar = wxT("IH0.CFFEX,IF0.CFFEX,IC0.CFFEX");
	}
	auto indexs = wxSplit(wxGetApp().GetFrameIndexBar(strIndexBar), wxT(','));
	for (auto it = indexs.begin(); it != indexs.end(); ++it)
	{
		const auto& index = *it;
		if (!index.empty()) {
			auto h = ZQDBGetCode(wxString2utf(index).c_str(), nullptr);
			if (h) {
				zqdb::Code code(h);
				//if (!code.IsSubscribeAll()) {
					if (!code.IsSubscribe()) {
						code.Subscribe();
					}
				//}
				IndexBarInfo info;
				info.h = h;
				info.name = utf2wxString(code->Name);
				indexs_.emplace_back(std::move(info));
			}
		}
	}
	widths.emplace_back(indexs_.size() * szIndex.x);
	//搜索
	//widths.emplace_back(200);
	//状态
	widths.emplace_back(24);
	//时间
	wxSize szTime = dc.GetTextExtent(wxT("09:25:53"));
	widths.emplace_back(szTime.x - 10);

	SetFieldsCount(widths.size(), widths.data());
	Update();
}

void MyStatusBar::Update()
{
	if (nStatus_ || ZQDBIsAnyDisabledModule() || ZQDBGetCalcAnyDisabledUser()) {
		nBmpStatus_ = 1;
	}
	else {
		nBmpStatus_ = 0;
	}
	Refresh();
}

wxBEGIN_EVENT_TABLE(MyStatusBar, Base)
	EVT_ERASE_BACKGROUND(MyStatusBar::OnErase)
	EVT_PAINT(MyStatusBar::OnPaint)
	EVT_SIZE(MyStatusBar::OnSize)
	EVT_TIMER(wxID_ANY, MyStatusBar::OnTimer)
	//EVT_IDLE(MyStatusBar::OnIdle)
	EVT_LEFT_UP(MyStatusBar::OnLeftUp)
	EVT_LEFT_DCLICK(MyStatusBar::OnLeftDClick)
wxEND_EVENT_TABLE()

void MyStatusBar::OnErase(wxEraseEvent &event)
{

}

void MyStatusBar::OnPaint(wxPaintEvent &event)
{
	wxBufferedPaintDC dc(this);
	dc.Clear();

	auto skin_info_ptr = std::static_pointer_cast<SkinInfo>(skin_info_ptr_);
	if (!skin_info_ptr) {
		return;
	}

	//auto style = GetWindowStyle();
	auto count = GetFieldsCount();
	for (auto i = 0; i < count; i++)
	{
		zqdb::Rect rect;
		if (!GetFieldRect(i, rect)) {
			continue;
		}
		if (rect.IsEmpty()) {
			continue;
		}
		wxDCClipper clipper(dc, rect);
		rect.DeflateX(skin_info_ptr->xySpace.x, skin_info_ptr->xySpace.x);

		auto alignment = wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL;
		if (i == count - 1) {
			dc.DrawLabel(wxDateTime::Now().FormatISOTime(), rect, wxALIGN_CENTER);
		}
		else if (i == count - 2) {
			auto bmpStatus = bmpStatus_[nBmpStatus_];
			if (bmpStatus.IsOk()) {
				dc.DrawBitmap(bmpStatus, rect.x
					, rect.y + (rect.GetHeight() - bmpStatus.GetHeight())/2, true);
				//rect.DeflateLeft(bmpStatus_.GetWidth());
			}
		}
		else if (i == count - 3) {
			auto text = GetStatusText(i);
			auto rcCalc = rect;
			dc.DrawLabel(text, wxNullBitmap, rect, alignment, -1, &rcCalc);
			rect.DeflateLeft(rcCalc.width);
			for(auto index = 0; index < indexs_.size(); index++)
			{
				auto& info = indexs_[index];
				info.rect = rect;
				zqdb::Code code(info.h);
				wxString strClose(wxT("——")), strZD(wxT("——")), strZDF(wxT("——"));
				double zd = 0., zdf = 0.;
				if (code.IsMarketValid(false)) {
					auto close = code->Close, yclose = ZQDBGetYClose(code, true);
					strClose = wxString::Format("%.2f", close);
					zd = close - yclose;
					zdf = (zd / yclose) * 100;
					strZD = wxString::Format("%+.2f", zd);
					strZDF = wxString::Format("%+.2f%%", zdf);
				}

				dc.DrawLabel(info.name, wxNullBitmap, rect, alignment, -1, &rcCalc);
				rect.DeflateLeft(rcCalc.width + skin_info_ptr->xySpace.x * 3);
				wxDCFontChanger fontChanger(dc, fontIndex_);
				wxDCTextColourChanger textColorChanger(dc, skin_info_ptr->GetCtrlColor(zd));
				dc.DrawLabel(strClose, wxNullBitmap, rect, alignment, -1, &rcCalc);
				rect.DeflateLeft(rcCalc.width + skin_info_ptr->xySpace.x);
				dc.DrawLabel(strZD, wxNullBitmap, rect, alignment, -1, &rcCalc);
				rect.DeflateLeft(rcCalc.width + skin_info_ptr->xySpace.x);
				dc.DrawLabel(strZDF, wxNullBitmap, rect, alignment, -1, &rcCalc);
				rect.DeflateLeft(rcCalc.width + skin_info_ptr->xySpace.x);
				info.rect.width = rect.x - info.rect.x;
				rect.DeflateLeft(skin_info_ptr->xySpace.x * 5);
			}
		}
		else {
			auto text = GetStatusText(i);
			if (text.IsEmpty()) {
				continue;
			}
			//auto field_style = GetStatusStyle(i);

			if (tips_highlight_) {
				tips_highlight_ = false;
				wxColor color = wxColor(255, 255, 255);
				switch (tips_level_)
				{
				case ZQDB_LOG_LEVEL_ERROR: {
					color = wxColor(217, 36, 48);
				} break;
				case ZQDB_LOG_LEVEL_WARN: {
					color = wxColor(255, 223, 114);
				} break;
				}
				dc.GradientFillLinear(rect, color, GetBackgroundColour());
			}
			//else 
			{
				if (tips_bmp_[tips_level_].IsOk()) {
					dc.DrawBitmap(tips_bmp_[tips_level_], rect.GetLeft(), rect.GetTop() + (rect.height - 16) / 2);
					rect.Offset(16 + 2, 0);
				}
				dc.DrawLabel(text, rect, alignment);
			}
		}
	}
}

void MyStatusBar::OnTimer(wxTimerEvent& event)
{
	Update();
}

void MyStatusBar::OnSize(wxSizeEvent& event)
{
	event.Skip();
	/*if (!search_ctrl_)
		return;

	auto count = GetFieldsCount();
	if (count > 3) {
		wxRect rect;
		if (GetFieldRect(count - 3, rect)) {
			search_ctrl_->Move(rect.x, rect.y);
		}
	}*/
}

void MyStatusBar::OnIdle(wxIdleEvent& event)
{
	//SetStatusText(numlockIndicators[wxGetKeyState(WXK_NUMLOCK)],
	//	Field_NumLockIndicator);
	//SetStatusText(capslockIndicators[wxGetKeyState(WXK_CAPITAL)],
	//	Field_CapsLockIndicator);

	event.Skip();
}

#include <zqdbmodule.h>

void MyStatusBar::OnLeftUp(wxMouseEvent& event)
{
	auto pt = event.GetPosition();
	for (int i = 0, j = GetFieldsCount(); i < j; i++)
	{
		wxRect rect;
		GetFieldRect(i, rect);
		if (!rect.Contains(pt))
			continue;
		if(i == 0) {
			auto size = GetClientSize();
			MyLogDlg::ShowDlg(GetScreenPosition(), size.x);
		}
		else if (i == j - 2) {
			wxMenu menu;
			for (size_t k = 0, n = ZQDBGetModuleCount(); k < n; k++)
			{
				if (k != 0) {
					menu.AppendSeparator();
				}
				auto hmodule = ZQDBGetModuleAt(k);
				zqdb::ObjectT<tagModuleInfoEx> module(hmodule);
				menu.Append(wxID_ANY, wxString::Format(wxT("%s %d"), utf2wxString(module->Name), module->tradingday));
				zqdb::AllModuleUser all_user(hmodule);
				for (auto huser : all_user)
				{
					zqdb::ObjectT<tagUserInfo> user(huser);
					wxString strStatus = utf2wxString(user->User) + wxT(".") + utf2wxString(module->Name) + wxT(" ") + zqdb::UserStatus2wxString(user->Status);
					menu.Append(wxID_ANY, strStatus);
				}
			}
			menu.AppendSeparator();
			for (size_t i = 0; i < all_status_.size(); i++)
			{
				zqdb::Status status(all_status_[i]);
				wxString strStatus = utf2wxString(status->Name) + wxT(" ") + zqdb::Status2wxString(status->Status);
				menu.Append(wxID_ANY, strStatus);
			}
			int reconnect = wxID_ANY;
			if (ZQDBClient() && !ZQDBIsConnect()) {
				menu.AppendSeparator();
				reconnect = menu.Append(wxID_ANY, wxT("重新登陆"))->GetId();
			}
			//
			menu.AppendSeparator();
			auto source_current = ZQDBGetMarketDataSource();
			auto source_default = menu.Append(wxID_ANY, wxT("默认行情源"), wxEmptyString, wxITEM_RADIO);
			if (source_current == MARKET_DATA_SOURCE_DEFAULT) {
				source_default->Check();
			}
			auto source_default_id = source_default->GetId();
			auto source1 = menu.Append(wxID_ANY, wxT("备用行情源1"), wxEmptyString, wxITEM_RADIO);
			if (source_current == MARKET_DATA_SOURCE_1) {
				source1->Check();
			}
			auto source1_id = source1->GetId();
			auto source2 = menu.Append(wxID_ANY, wxT("备用行情源2"), wxEmptyString, wxITEM_RADIO);
			if (source_current == MARKET_DATA_SOURCE_2) {
				source2->Check();
			}
			auto source2_id = source2->GetId();
			auto source3 = menu.Append(wxID_ANY, wxT("备用行情源3"), wxEmptyString, wxITEM_RADIO);
			if (source_current == MARKET_DATA_SOURCE_3) {
				source3->Check();
			}
			auto source3_id = source3->GetId();
			auto id = this->GetPopupMenuSelectionFromUser(menu, rect.GetLeftTop());
			if (reconnect == id) {
				ZQDBReConnect();
			}
			else if (source_default_id == id) {
				ZQDBSetMarketDataSource(MARKET_DATA_SOURCE_DEFAULT);
			}
			else if (source1_id == id) {
				ZQDBSetMarketDataSource(MARKET_DATA_SOURCE_1);
			}
			else if (source2_id == id) {
				ZQDBSetMarketDataSource(MARKET_DATA_SOURCE_2);
			}
			else if (source3_id == id) {
				ZQDBSetMarketDataSource(MARKET_DATA_SOURCE_3);
			}
		}
		else if (i == j - 3) {
			for (const auto& info : indexs_)
			{
				if (info.rect.Contains(pt)) {
					wxGetApp().Goto(info.h, GetParent());
					break;
				}
			}
		}
		break;
	}
}

void MyStatusBar::OnLeftDClick(wxMouseEvent& event)
{
	event.Skip();
}

///

wxBEGIN_EVENT_TABLE(MyTechFrame, Base)
//Navigate
//EVT_RIBBONBUTTONBAR_CLICKED(ID_NAVIGATE_HOME, MyTechFrame::OnNavigateHome)
EVT_RIBBONBUTTONBAR_CLICKED(ID_NAVIGATE_BACKWARD, MyTechFrame::OnNavigateBackward)
EVT_RIBBONBUTTONBAR_DROPDOWN_CLICKED(ID_NAVIGATE_BACKWARD, MyTechFrame::OnNavigateBackwardDropdown)
EVT_RIBBONBUTTONBAR_CLICKED(ID_NAVIGATE_FORWARD, MyTechFrame::OnNavigateForward)
EVT_RIBBONBUTTONBAR_DROPDOWN_CLICKED(ID_NAVIGATE_FORWARD, MyTechFrame::OnNavigateForwardDropdown)
EVT_RIBBONBUTTONBAR_CLICKED(ID_NAVIGATE_ALL, MyTechFrame::OnNavigateAll)
EVT_RIBBONBUTTONBAR_DROPDOWN_CLICKED(ID_NAVIGATE_ALL, MyTechFrame::OnNavigateAllDropdown)
EVT_UPDATE_UI(ID_NAVIGATE_BACKWARD, MyTechFrame::OnNavigateBackwardUpdateUI)
EVT_UPDATE_UI(ID_NAVIGATE_FORWARD, MyTechFrame::OnNavigateForwardUpdateUI)
//EVT_RIBBONBUTTONBAR_CLICKED(ID_NAVIGATE_UP, MyTechFrame::OnNavigateUp)
//EVT_RIBBONBUTTONBAR_CLICKED(ID_NAVIGATE_DOWN, MyTechFrame::OnNavigateDown)
EVT_RIBBONBUTTONBAR_CLICKED(ID_NAVIGATE_FIND, MyTechFrame::OnNavigateFind)
EVT_RIBBONBUTTONBAR_DROPDOWN_CLICKED(ID_NAVIGATE_FIND, MyTechFrame::OnNavigateFindDropdown)
EVT_MENU_RANGE(ID_NAVIGATE_FIND_SYSTEM, ID_NAVIGATE_FIND_SYSTEM_MAX, MyTechFrame::OnNavigateFindSystemGoto)
EVT_RIBBONBUTTONBAR_CLICKED(ID_NAVIGATE_FIND_SIMILARITY, MyTechFrame::OnNavigateFindSimilarity)
EVT_MENU_RANGE(ID_NAVIGATE_FIND_SIMILARITY, ID_NAVIGATE_FIND_SIMILARITY_CLOUD, MyTechFrame::OnNavigateFindSimilarityGoto)
//Market
EVT_RIBBONBUTTONBAR_CLICKED(ID_MARKET_ALL, MyTechFrame::OnMarketAll)
EVT_RIBBONBUTTONBAR_DROPDOWN_CLICKED(ID_MARKET_ALL, MyTechFrame::OnMarketAllDropdown)
//EVT_RIBBONBUTTONBAR_CLICKED(ID_MARKET_SELF, MyTechFrame::OnMarket)
//EVT_RIBBONBUTTONBAR_CLICKED(ID_MARKET_MAIN, MyTechFrame::OnMarketSys)
//EVT_RIBBONBUTTONBAR_CLICKED(ID_MARKET_ALL, MyTechFrame::OnMarketAll)
////Trade
//EVT_RIBBONBUTTONBAR_CLICKED(ID_TRADE_QUICK_BUY_FAK, MyTechFrame::OnTradeBuyFAK)
//EVT_RIBBONBUTTONBAR_CLICKED(ID_TRADE_QUICK_BUY_FOK, MyTechFrame::OnTradeBuyFOK)
//EVT_RIBBONBUTTONBAR_CLICKED(ID_TRADE_QUICK_SELL_FAK, MyTechFrame::OnTradeSellFAK)
//EVT_RIBBONBUTTONBAR_CLICKED(ID_TRADE_QUICK_SELL_FOK, MyTechFrame::OnTradeSellFOK)
//EVT_RIBBONBUTTONBAR_CLICKED(ID_TRADE_QUICK_CLOSE, MyTechFrame::OnTradeClose)
//EVT_RIBBONBUTTONBAR_CLICKED(ID_TRADE_POSITION, MyTechFrame::OnTradePosition)
//EVT_RIBBONBUTTONBAR_CLICKED(ID_TRADE_ORDER, MyTechFrame::OnTradeOrder)
//EVT_RIBBONBUTTONBAR_CLICKED(ID_TRADE_TRADE, MyTechFrame::OnTradeTrade)
//EVT_RIBBONBUTTONBAR_CLICKED(ID_TRADE_ADDUSER, MyTechFrame::OnTradeAddUser)
//EVT_RIBBONBUTTONBAR_CLICKED(ID_TRADE_REMOVEUSER, MyTechFrame::OnTradeRemoveUser)
EVT_RIBBONBUTTONBAR_CLICKED(ID_CLEAR_SETTINGS, MyTechFrame::OnClearSettings)
EVT_RIBBONBUTTONBAR_CLICKED(ID_CLEAR_DATA, MyTechFrame::OnClearData)
EVT_MENU_RANGE(ID_CALC_EDIT_NEW, ID_CALC_EDIT_NEW_MAX, MyTechFrame::OnCalcEditNew)
EVT_MENU_RANGE(ID_CALC_EDIT_MAJOR, ID_CALC_EDIT_MAJOR_MAX, MyTechFrame::OnCalcEditOpen)
EVT_MENU_RANGE(ID_CALC_EDIT_MINOR, ID_CALC_EDIT_MINOR_MAX, MyTechFrame::OnCalcEditOpen)
EVT_MENU_RANGE(ID_CALC_EDIT_CONTAINER, ID_CALC_EDIT_CONTAINER_MAX, MyTechFrame::OnCalcEditOpen)
EVT_MENU_RANGE(ID_CALC_EDIT_FILTER, ID_CALC_EDIT_FILTER_MAX, MyTechFrame::OnCalcEditOpen)
EVT_MENU_RANGE(ID_CALC_EDIT_SORT, ID_CALC_EDIT_SORT_MAX, MyTechFrame::OnCalcEditOpen)
EVT_MENU_RANGE(ID_CALC_EDIT_SCRIPT, ID_CALC_EDIT_SCRIPT_MAX, MyTechFrame::OnCalcEditOpen)
EVT_MENU_RANGE(ID_CALC_EDIT_STRATEGY, ID_CALC_EDIT_STRATEGY_MAX, MyTechFrame::OnCalcEditOpen)

EVT_TIMER(wxID_ANY, MyTechFrame::OnTimer)
wxEND_EVENT_TABLE()
//
//wxBEGIN_EVENT_TABLE(MyTechFrame::EventHandler, wxEvtHandler)
//EVT_CHAR(MyTechFrame::EventHandler::OnChar)
//wxEND_EVENT_TABLE()
//
//MyTechFrame::EventHandler::EventHandler(MyTechFrame* frame)
//{
//	wxWindowList children = frame->GetChildren();
//	for (wxWindowListNode *i = children.GetFirst(); i; i = i->GetNext()) {
//		SetChildNextHandler(i->GetData());
//	}
//}
//
//void MyTechFrame::EventHandler::SetChildNextHandler(wxWindow* child)
//{
//	if (child) {
//		child->SetNextHandler(this);
//		wxWindowList children = child->GetChildren();
//		for (wxWindowListNode *i = children.GetFirst(); i; i = i->GetNext()) {
//			SetChildNextHandler(i->GetData());
//		}
//	}
//}
//
//void MyTechFrame::EventHandler::OnChar(wxKeyEvent &event)
//{
//	event.Skip();
//}

MyTechFrame* MyTechFrame::GetFrameByChild(wxWindow* child)
{
	if (child) {
		wxWindow* parent = nullptr;
		while (parent = child->GetParent()) {
			child = parent;
		}
		//return wxDynamicCast(child, MyTechFrame);
		return wxStaticCast(child, MyTechFrame);
	}
	return nullptr;
}

MyTechFrame::MyTechFrame(const char* xml, size_t xmlflag)
    : Base(xml, xmlflag)
	, all_container_func_(CALC_CONTAINER)
	, all_filter_func_(CALC_FILTER)
#ifdef _SORT
	, all_sort_func_(CALC_SORT)
#endif
	, all_script_func_(CALC_SCRIPT)
{
#ifdef _DEBUG
	auto cwd = boost::filesystem::current_path();
#endif//
	CFG_FROM_XML(cfg, xml, xmlflag);

	auto container_calc_func = wxGetApp().GetContainerCalcFunc();
	for (size_t i = 0; i < all_container_func_.size(); i++)
	{
		zqdb::Calc::Func func(all_container_func_[i]);
		wxString name = func.GetCalcName();
		if (name == container_calc_func) {
			cur_container_func_ = i;
			break;
		}
	}
#ifdef _SORT
	auto sort_calc_func = wxGetApp().GetSortCalcFunc();
	for (size_t i = 0; i < all_sort_func_.size(); i++)
	{
		zqdb::Calc::Func func(all_sort_func_[i]);
		wxString name = func.GetCalcName();
		if (name == sort_calc_func) {
			cur_sort_func_ = i;
			break;
		}
	}
#endif

	//CreateStatusBar();
	status_bar_ = new MyStatusBar(this);
	SetStatusBar(status_bar_);
	//SetStatusText("no selection", 0);

    m_ribbon->Realize();

    //m_logwindow = new wxTextCtrl(this, wxID_ANY, wxEmptyString,
    //    wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE | wxTE_READONLY |
    //    wxTE_LEFT | wxTE_BESTWRAP | wxBORDER_NONE);
	//m_logwindow->Show(false);

	code_view_ = new MyCodeView(this);

#ifdef _TRADE
	user_view_ = new MyUserView(this);
#endif//_TRADE

	/*auto opt_htv = cfg.get_child_optional("htmlview");
	if (opt_htv) {
	auto& cfg_htv = opt_htv.get();
	auto htv = new wxHtmlWindow(this);
	//htv->SetRelatedFrame(this, _("wxHTML Demo: '%s'"));
	#if wxUSE_STATUSBAR
	//htv->SetRelatedStatusBar(1);
	#endif // wxUSE_STATUSBAR
	htv->LoadPage(cfg_htv.get<std::string>("url"));
	}
	else */ {
		auto opt_tv = cfg.get_child_optional("techview");
		ASSERT(opt_tv);
		auto& cfg_tv = opt_tv.get();
		auto kscale = wxGetApp().GetTechKScale();
		cfg_tv.put("kscale", kscale);
		auto ktype = wxGetApp().GetTechKType();
		cfg_tv.put("ktype", ktype);
		CALC_STICK_TYPE kvtype = CALC_STICK_LINE;
		if(ktype == CALC_KLINE_K) {
			kvtype = CALC_STICK_BAR;
		}
		cfg_tv.put("kvtype", kvtype);
		auto cycle = wxGetApp().GetTechCycleCur(g_full ? CYC_DAY : CYC_1MIN);
		size_t cycleex = 0;
		switch (cycle)
		{
#if USE_CYC_SEC
		case CYC_ANYSEC: {
			cycleex = wxGetApp().GetTechCycleAnySec();
		} break;
#endif
		case CYC_ANYMIN: {
			cycleex = wxGetApp().GetTechCycleAnyMin();
		} break;
		}
		cfg_tv.put("cycle", cycle);
		cfg_tv.put("cycleex", cycleex);
		auto tv = new MyTechView(this, (const char*)&cfg_tv, XUtil::XML_FLAG_PTREE);
		tech_view_ = tv;
		//tech_view_->SetScrollbars(10, 10, 100, 240);
	}

	//m_togglePanels = new wxToggleButton(this, wxID_ANY, "&Toggle panels");
	//m_togglePanels->SetValue(true);
	//m_togglePanels->Show(false);

	wxSizer *s = new wxBoxSizer(wxVERTICAL);

	s->Add(m_ribbon, 0, wxEXPAND);

#if wxUSE_INFOBAR
	s->Add(infobar_, wxSizerFlags().Expand());
#endif//

	wxSizer *sh = new wxBoxSizer(wxHORIZONTAL);

	//sh->Add(m_logwindow, 0, wxEXPAND);
	sh->Add(code_view_, 0, wxEXPAND);
	sh->AddSpacer(1);

	/*auto opt_topview = cfg.get_child_optional("topview");
	if (opt_topview) {
		auto& cfg_topview = opt_topview.get();
		auto *sizer_topview = new wxBoxSizer(wxHORIZONTAL);
		auto opt_tradeview = cfg_topview.get_child_optional("tradeview");
		auto opt_infoview = cfg_topview.get_child_optional("infoview");
		if (opt_tradeview) {
			trade_view_ = new TradeView(this, (const char*)&opt_tradeview.get(), XUtil::XML_FLAG_PTREE);
			sizer_topview->Add(trade_view_, 0, wxEXPAND);
		}
		if (opt_infoview) {
			info_view_ = new InfoView(this, (const char*)&opt_infoview.get(), XUtil::XML_FLAG_PTREE);
			sizer_topview->Add(info_view_, 1, wxEXPAND);
		}
		else {
			info_view_ = new InfoView(this);
			sizer_topview->Add(info_view_, 1, wxEXPAND);
		}
		tech_sizer_->Add(sizer_topview, 0, wxEXPAND);
	}*/

	sh->Add(tech_view_, 1, wxEXPAND);

	//右侧视图
	/*sh->AddSplitter(1);
	//sh->AddSpacer(200);
	sh->Add(
	new wxButton(this, wxID_CANCEL, "Cancel"),
	1,
	wxEXPAND);*/
	auto opt_rightview = cfg.get_child_optional("rightview");
	if (opt_rightview) {
		bool add_spacer = false;
		auto& cfg_rightview = opt_rightview.get();
		auto *sizer_rightview = new wxBoxSizer(wxVERTICAL);
		auto opt_titleview = cfg_rightview.get_child_optional("titleview");
		auto opt_mmpview = cfg_rightview.get_child_optional("mmpview");
		auto opt_tradeview = cfg_rightview.get_child_optional("tradeview");
		auto opt_infoview = cfg_rightview.get_child_optional("infoview");
		auto opt_tickview = cfg_rightview.get_child_optional("tickview");
		if (opt_titleview) {
			if (add_spacer) {
				sizer_rightview->AddSpacer(1);
			}
			add_spacer = true;
			title_view_ = new zqdb::TitleView(this, (const char*)&opt_titleview.get(), XUtil::XML_FLAG_PTREE);
			sizer_rightview->Add(title_view_, 0, wxEXPAND);
		}
		if (opt_mmpview) {
			if (add_spacer) {
				sizer_rightview->AddSpacer(1);
			}
			add_spacer = true;
			mmp_view_ = new zqdb::MmpView(this, (const char*)&opt_mmpview.get(), XUtil::XML_FLAG_PTREE);
			sizer_rightview->Add(mmp_view_, 0, wxEXPAND);
		}
		if (opt_tradeview) {
			if (add_spacer) {
				sizer_rightview->AddSpacer(1);
			}
			add_spacer = true;
			trade_view_ = new zqdb::TradeView(this, (const char*)&opt_tradeview.get(), XUtil::XML_FLAG_PTREE);
			sizer_rightview->Add(trade_view_, 0, wxEXPAND);
		}
		if (opt_infoview) {
			if (add_spacer) {
				sizer_rightview->AddSpacer(1);
			}
			add_spacer = true;
			info_view_ = new zqdb::InfoView(this, (const char*)&opt_infoview.get(), XUtil::XML_FLAG_PTREE);
			sizer_rightview->Add(info_view_, 0, wxEXPAND);
		}
		if (opt_tickview) {
			if (add_spacer) {
				sizer_rightview->AddSpacer(1);
			}
			add_spacer = true;
			tick_view_ = new zqdb::TickView(this, (const char*)&opt_tickview.get(), XUtil::XML_FLAG_PTREE);
			sizer_rightview->Add(tick_view_, 1, wxEXPAND);
		}
		else {
			if (add_spacer) {
				sizer_rightview->AddSpacer(1);
			}
			add_spacer = true;
			tick_view_ = new zqdb::TickView(this);
			sizer_rightview->Add(tick_view_, 1, wxEXPAND);
		}
		sh->AddSpacer(1);
		sh->Add(sizer_rightview, 0, wxEXPAND);
	}

	s->Add(sh, 1, wxEXPAND);

	if (user_view_) {
		s->Add(user_view_, 0, wxEXPAND);
	}

	SetSizer(s);

	Bind(wxEVT_RIBBONBUTTONBAR_CLICKED, &MyTechFrame::OnMarketExchange, this, ID_MARKET_EXCHANGE, ID_MARKET_EXCHANGE_MAX);
	Bind(wxEVT_RIBBONBUTTONBAR_DROPDOWN_CLICKED, &MyTechFrame::OnExchangeDropdown, this, ID_MARKET_EXCHANGE, ID_MARKET_EXCHANGE_MAX);
	Bind(wxEVT_RIBBONBUTTONBAR_CLICKED, &MyTechFrame::OnMarketProduct, this, ID_MARKET_PRODUCT, ID_MARKET_PRODUCT_MAX);
	Bind(wxEVT_RIBBONBUTTONBAR_CLICKED, &MyTechFrame::OnMarketSelf, this, ID_MARKET_SELF);
	Bind(wxEVT_RIBBONBUTTONBAR_CLICKED, &MyTechFrame::OnMarketSubscribe, this, ID_MARKET_SUBSCRIBE);
	Bind(wxEVT_RIBBONBUTTONBAR_DROPDOWN_CLICKED, &MyTechFrame::OnMarketSubscribeDropdown, this, ID_MARKET_SUBSCRIBE);
	Bind(wxEVT_RIBBONBUTTONBAR_CLICKED, &MyTechFrame::OnMarketCategory, this, ID_MARKET_CATEGORY, ID_MARKET_CATEGORY_MAX);
	Bind(wxEVT_RIBBONBUTTONBAR_DROPDOWN_CLICKED, &MyTechFrame::OnMarketCategoryDropdown, this, ID_MARKET_CATEGORY, ID_MARKET_CATEGORY_MAX);
	Bind(wxEVT_RIBBONBUTTONBAR_CLICKED, &MyTechFrame::OnContainer, this, ID_CONTAINER, ID_CONTAINER_MAX);
	Bind(wxEVT_RIBBONBUTTONBAR_DROPDOWN_CLICKED, &MyTechFrame::OnContainerDropdown, this, ID_CONTAINER, ID_CONTAINER_MAX);
	Bind(wxEVT_RIBBONBUTTONBAR_CLICKED, &MyTechFrame::OnFilter, this, ID_FILTER, ID_FILTER_MAX);
	Bind(wxEVT_RIBBONBUTTONBAR_DROPDOWN_CLICKED, &MyTechFrame::OnFilterDropdown, this, ID_FILTER, ID_FILTER_MAX);
#ifdef _SORT
	Bind(wxEVT_RIBBONBUTTONBAR_CLICKED, &MyTechFrame::OnSort, this, ID_SORT, ID_SORT_QUICK_MAX);
	Bind(wxEVT_RIBBONBUTTONBAR_CLICKED, &MyTechFrame::OnSort, this, ID_SORT_CALC, ID_SORT_CALC_MAX);
	Bind(wxEVT_RIBBONBUTTONBAR_DROPDOWN_CLICKED, &MyTechFrame::OnSortDropdown, this, ID_SORT, ID_SORT_QUICK_MAX);
	Bind(wxEVT_RIBBONBUTTONBAR_DROPDOWN_CLICKED, &MyTechFrame::OnSortDropdown, this, ID_SORT_CALC, ID_SORT_CALC_MAX);
#endif
	Bind(wxEVT_RIBBONTOOLBAR_CLICKED, &MyTechFrame::OnTechKLine, this, ID_TECHVIEW_KLINE, ID_TECHVIEW_KLINE_MAX);
	Bind(wxEVT_RIBBONTOOLBAR_CLICKED, &MyTechFrame::OnTechCycle, this, ID_TECHVIEW_CYCLE, ID_TECHVIEW_CYCLE_MAX);
	Bind(wxEVT_RIBBONTOOLBAR_DROPDOWN_CLICKED, &MyTechFrame::OnTechCycleDropdown, this, ID_TECHVIEW_CYCLE, ID_TECHVIEW_CYCLE_MAX);
	Bind(wxEVT_RIBBONTOOLBAR_CLICKED, &MyTechFrame::OnTechMove, this, ID_TECHVIEW_MOVE_AUTO, ID_TECHVIEW_MOVE_SHIFT);
	Bind(wxEVT_RIBBONTOOLBAR_CLICKED, &MyTechFrame::OnTechDrawline, this, ID_TECHVIEW_DRAWLINE, ID_TECHVIEW_DRAWLINE_MAX);
	Bind(wxEVT_RIBBONTOOLBAR_DROPDOWN_CLICKED, &MyTechFrame::OnTechDrawlineDropdown, this, ID_TECHVIEW_DRAWLINE, ID_TECHVIEW_DRAWLINE_MAX);
	Bind(wxEVT_RIBBONTOOLBAR_CLICKED, &MyTechFrame::OnTechZoom, this, ID_TECHVIEW_ZOOM_IN, ID_TECHVIEW_ZOOM_RESET);
	Bind(wxEVT_RIBBONTOOLBAR_DROPDOWN_CLICKED, &MyTechFrame::OnTechViewDropdown, this, ID_TECHVIEW_VIEW);
	Bind(wxEVT_RIBBONTOOLBAR_CLICKED, &MyTechFrame::OnF10, this, ID_INFO_F10, ID_INFO_F10_MAX);
	Bind(wxEVT_RIBBONBUTTONBAR_CLICKED, &MyTechFrame::OnScript, this, ID_SCRIPT, ID_SCRIPT_MAX);
	Bind(wxEVT_RIBBONBUTTONBAR_DROPDOWN_CLICKED, &MyTechFrame::OnScriptDropdown, this, ID_SCRIPT, ID_SCRIPT_MAX);

	Bind(wxEVT_MENU, &MyTechFrame::OnNavigateGoto, this, ID_NAVIGATE_GOTO, ID_NAVIGATE_GOTO_MAX);
	Bind(wxEVT_MENU, &MyTechFrame::OnNavigateAllGoto, this, ID_NAVIGATE_EXCHANGE, ID_NAVIGATE_EXCHANGE_MAX);
	Bind(wxEVT_MENU, &MyTechFrame::OnNavigateAllGoto, this, ID_NAVIGATE_EXCHANGE_PRODUCT, ID_NAVIGATE_EXCHANGE_PRODUCT_MAX);
	Bind(wxEVT_MENU, &MyTechFrame::OnMarketAllGoto, this, ID_MARKET_EXCHANGE, ID_MARKET_EXCHANGE_MAX);
	Bind(wxEVT_MENU, &MyTechFrame::OnMarketAllGoto, this, ID_MARKET_EXCHANGE_PRODUCT, ID_MARKET_EXCHANGE_PRODUCT_MAX);
	Bind(wxEVT_MENU, &MyTechFrame::OnProductGoto, this, ID_MARKET_PRODUCT, ID_MARKET_PRODUCT_MAX);
	Bind(wxEVT_MENU, &MyTechFrame::OnMarketSubscribeGoto, this, ID_MARKET_SELF);
	Bind(wxEVT_MENU, &MyTechFrame::OnMarketCategoryGoto, this, ID_MARKET_CATEGORY_MENU, ID_MARKET_CATEGORY_MENU_MAX);
	Bind(wxEVT_MENU, &MyTechFrame::OnMarketCategoryGoto, this, ID_MARKET_CATEGORY, ID_MARKET_CATEGORY_MAX);
	Bind(wxEVT_MENU, &MyTechFrame::OnContainerGoto, this, ID_CONTAINER_MENU, ID_CONTAINER_MENU_MAX);
	Bind(wxEVT_MENU, &MyTechFrame::OnContainerGoto, this, ID_CONTAINER, ID_CONTAINER_MAX);
	Bind(wxEVT_MENU, &MyTechFrame::OnFilterGoto, this, ID_FILTER_MENU, ID_FILTER_MENU_MAX);
	Bind(wxEVT_MENU, &MyTechFrame::OnFilterGoto, this, ID_FILTER, ID_FILTER_MAX);
#ifdef _SORT
	Bind(wxEVT_MENU, &MyTechFrame::OnSortGoto, this, ID_SORT, ID_SORT_QUICK_MAX);
	Bind(wxEVT_MENU, &MyTechFrame::OnSortGoto, this, ID_SORT_CALC, ID_SORT_CALC_MAX);
#endif
	Bind(wxEVT_MENU, &MyTechFrame::OnTechCycleGoto, this, ID_TECHVIEW_CYCLE, ID_TECHVIEW_CYCLE_MAX);
	Bind(wxEVT_MENU, &MyTechFrame::OnTechDrawlineGoto, this, ID_TECHVIEW_DRAWLINE, ID_TECHVIEW_DRAWLINE_MAX);
	Bind(wxEVT_MENU, &MyTechFrame::OnTechViewGoto, this, ID_TECHVIEW_VIEW, ID_TECHVIEW_VIEW_MAX);
	Bind(wxEVT_MENU, &MyTechFrame::OnScriptGoto, this, ID_SCRIPT, ID_SCRIPT_MAX);
	Bind(wxEVT_MENU, &MyTechFrame::OnScriptGoto, this, ID_SCRIPT_MENU, ID_SCRIPT_MENU_MAX);

	/*Bind(ZQDB_NOTIFY_ENABLE_EVENT, &MyTechFrame::OnNotify, this);
	Bind(ZQDB_NOTIFY_DISABLE_EVENT, &MyTechFrame::OnNotify, this);
	Bind(ZQDB_NOTIFY_APPEND_EVENT, &MyTechFrame::OnNotify, this);
	Bind(ZQDB_NOTIFY_REMOVE_EVENT, &MyTechFrame::OnNotify, this);
	Bind(ZQDB_NOTIFY_UPDATE_EVENT, &MyTechFrame::OnNotify, this);*/

	// test that event handlers pushed on top of MDI children do work (this
	// used to be broken, see #11225)
	//PushEventHandler(new EventHandler(this));
}

///

void MyTechView::OnSelectRightDown(zqdb::Windicator* win, const wxPoint& pt)
{
	enum {
		ID_FUNC_SEARCH = wxID_HIGHEST,
	};
	wxMenu menu;
	menu.Append(ID_NAVIGATE_FIND_SIMILARITY, wxT("相似选股"));
	menu.Append(ID_NAVIGATE_FIND_SIMILARITY_CLOUD, wxT("云相似选股"));
	int id = win->GetPopupMenuSelectionFromUser(menu, pt);
	switch (id)
	{
	case ID_NAVIGATE_FIND_SIMILARITY:
	case ID_NAVIGATE_FIND_SIMILARITY_CLOUD: {
		size_t pos_start = 0, pos_end = 0;
		win->GetDispPointValue(win->GetSelectStart(), pos_start);
		win->GetDispPointValue(win->GetSelectEnd(), pos_end);
		if (pos_end < pos_start) {
			auto pos = pos_start;
			pos_start = pos_end;
			pos_end = pos;
		}
		MyTechFrame::GetFrameByChild(this)->ShowSimilarity(
			id == ID_NAVIGATE_FIND_SIMILARITY_CLOUD ? wxT("云相似选股") : wxT("相似选股")
			, pos_end - pos_start, pos_start, win
			, id == ID_NAVIGATE_FIND_SIMILARITY_CLOUD ? MyTechFrame::SHOW_SIMILARITY_FLAG_CLOUD : 0);
	} break;
	default: {
	} break;
	}
}

///

class MyF10Frame : public MyWebFrame
{
public:
	using MyWebFrame::MyWebFrame;
	~MyF10Frame() {
		auto parent = (MyTechFrame*)GetParent();
		if (parent) {
			parent->f10_frame_ = nullptr;
		}
	}

	wxString key;
};

///

MyTechFrame::~MyTechFrame()
{
	//PopEventHandler(true);
	UnSubscribe(Get());
	wxGetApp().ResetFrame(this);
}

void MyTechFrame::AddFirstPages()
{
	wxRibbonPage* home = new wxRibbonPage(m_ribbon, wxID_ANY, wxT("开始"));

	auto navigate_panel = new wxRibbonPanel(home, wxID_ANY, wxT("导航"), wxNullBitmap, wxDefaultPosition, wxDefaultSize
		, wxRIBBON_PANEL_NO_AUTO_MINIMISE | wxRIBBON_PANEL_EXT_BUTTON);
	navigate_panel->Bind(wxEVT_RIBBONPANEL_EXTBUTTON_ACTIVATED, &MyTechFrame::OnNavigateSetting, this);
	navigate_bar_ = new wxRibbonButtonBar(navigate_panel);
	InnerUpdateNavigate();

	/*auto fast_access_panel = new wxRibbonPanel(home, wxID_ANY, "快速访问");
	wxRibbonButtonBar* fast_access_bar = new wxRibbonButtonBar(fast_access_panel);
	fast_access_bar->AddButton(ID_MARKET_ALL, "全部", skin_info_ptr_->GetBitmap32(wxEmptyString));
	fast_access_bar->SetButtonMaxSizeClass(ID_MARKET_ALL, wxRIBBON_BUTTONBAR_BUTTON_MEDIUM);
	fast_access_bar->SetButtonMinSizeClass(ID_MARKET_ALL, wxRIBBON_BUTTONBAR_BUTTON_MEDIUM);
	fast_access_bar->AddButton(ID_MARKET_MAIN, "主力", skin_info_ptr_->GetBitmap32(wxEmptyString));
	fast_access_bar->SetButtonMaxSizeClass(ID_MARKET_MAIN, wxRIBBON_BUTTONBAR_BUTTON_MEDIUM);
	fast_access_bar->SetButtonMinSizeClass(ID_MARKET_MAIN, wxRIBBON_BUTTONBAR_BUTTON_MEDIUM);
	fast_access_bar->AddButton(ID_MARKET_SELF, "自选", skin_info_ptr_->GetBitmap32(wxEmptyString), wxEmptyString);
	fast_access_bar->SetButtonMaxSizeClass(ID_MARKET_SELF, wxRIBBON_BUTTONBAR_BUTTON_MEDIUM);
	fast_access_bar->SetButtonMinSizeClass(ID_MARKET_SELF, wxRIBBON_BUTTONBAR_BUTTON_MEDIUM);
	fast_access_bar->AddButton(ID_TRADE_POSITION, "持仓", skin_info_ptr_->GetBitmap32(wxEmptyString));
	fast_access_bar->SetButtonMaxSizeClass(ID_TRADE_POSITION, wxRIBBON_BUTTONBAR_BUTTON_MEDIUM);
	fast_access_bar->SetButtonMinSizeClass(ID_TRADE_POSITION, wxRIBBON_BUTTONBAR_BUTTON_MEDIUM);
	fast_access_bar->AddButton(ID_TRADE_ORDER, "委托", skin_info_ptr_->GetBitmap32(wxEmptyString));
	fast_access_bar->SetButtonMaxSizeClass(ID_TRADE_ORDER, wxRIBBON_BUTTONBAR_BUTTON_MEDIUM);
	fast_access_bar->SetButtonMinSizeClass(ID_TRADE_ORDER, wxRIBBON_BUTTONBAR_BUTTON_MEDIUM);
	fast_access_bar->AddButton(ID_TRADE_TRADE, "成交", skin_info_ptr_->GetBitmap32(wxEmptyString));
	fast_access_bar->SetButtonMaxSizeClass(ID_TRADE_TRADE, wxRIBBON_BUTTONBAR_BUTTON_MEDIUM);
	fast_access_bar->SetButtonMinSizeClass(ID_TRADE_TRADE, wxRIBBON_BUTTONBAR_BUTTON_MEDIUM);*/

	//auto market_panel = new wxRibbonPanel(home, wxID_ANY, "快速导航");
	//wxRibbonButtonBar* market_bar = new wxRibbonButtonBar(market_panel);
	//int product_id = ID_MARKET_PRODUCT;
	//for (size_t i = 0; i < allexchange.size(); i++)
	//{
	//	zqdb::Exchange exchange(allexchange[i]);
	//	//auto item = market_bar->AddButton(ID_MARKET_EXCHANGE + i, utf2wxString(exchange->Exchange), skin_info_ptr_->GetBitmap32(wxEmptyString));
	//	//market_bar->SetItemClientData(item, allexchange[i]);
	//	//market_bar->SetButtonMaxSizeClass(ID_MARKET_EXCHANGE + i, wxRIBBON_BUTTONBAR_BUTTON_MEDIUM);
	//	//market_bar->SetButtonMinSizeClass(ID_MARKET_EXCHANGE + i, wxRIBBON_BUTTONBAR_BUTTON_MEDIUM);
	//	/*auto market_panel = new wxRibbonPanel(home, ID_MARKET_EXCHANGE + i, utf2wxString(exchange->Exchange));
	//	market_panel->SetToolTip(utf2wxString(exchange->Name));
	//	wxRibbonButtonBar* market_bar = new wxRibbonButtonBar(market_panel);*/
	//	////这里可以显示最活跃品种
	//	//zqdb::AllProduct allproduct(allexchange[i]);
	//	///*std::sort(allproduct.begin(), allproduct.end(), [](HZQDB x, HZQDB y) {
	//	//	zqdb::Product xproduct(x);
	//	//	zqdb::Product yproduct(y);
	//	//	return strcmp(xproduct->Name, yproduct->Name) < 0;
	//	//});*/
	//	//for (size_t k = 0; k < allproduct.size() && k < 2; k++)
	//	//{
	//	//	zqdb::Product product(allproduct[k]);
	//	//	auto item = market_bar->AddButton(product_id, utf2wxString(product->Name), skin_info_ptr_->GetBitmap32(wxEmptyString));
	//	//	market_bar->SetItemClientData(item, allproduct[k]);
	//	//	market_bar->SetButtonMaxSizeClass(product_id, wxRIBBON_BUTTONBAR_BUTTON_MEDIUM);
	//	//	market_bar->SetButtonMinSizeClass(product_id, wxRIBBON_BUTTONBAR_BUTTON_MEDIUM);
	//	//	++product_id;
	//	//}
	//	//这里显示市场下拉菜单，可以查看所有品种
	//	auto item = market_bar->AddHybridButton(ID_MARKET_EXCHANGE + i
	//		, utf2wxString(exchange->Exchange), skin_info_ptr_->GetBitmap32(wxEmptyString));
	//	market_bar->SetItemClientData(item, allexchange[i]);
	//	market_bar->SetButtonMaxSizeClass(ID_MARKET_EXCHANGE + i, wxRIBBON_BUTTONBAR_BUTTON_LARGE);
	//	market_bar->SetButtonMinSizeClass(ID_MARKET_EXCHANGE + i, wxRIBBON_BUTTONBAR_BUTTON_LARGE);
	//}
	//ASSERT(product_id < ID_MARKET_PRODUCT_MAX);

	/*auto filter_panel = new wxRibbonPanel(home, wxID_ANY, wxT("筛选"), wxNullBitmap, wxDefaultPosition, wxDefaultSize
		, wxRIBBON_PANEL_NO_AUTO_MINIMISE | wxRIBBON_PANEL_EXT_BUTTON);
	filter_panel->Bind(wxEVT_RIBBONPANEL_EXTBUTTON_ACTIVATED, &MyTechFrame::OnFilterSetting, this);
	filter_bar_ = new wxRibbonButtonBar(filter_panel);
	InnerUpdateFilter();*/

#ifdef _SORT
	//点击一次降序、二次升序、三次不排序
	auto sort_panel = new wxRibbonPanel(home, wxID_ANY, wxT("排序"), wxNullBitmap, wxDefaultPosition, wxDefaultSize
		, wxRIBBON_PANEL_NO_AUTO_MINIMISE | wxRIBBON_PANEL_EXT_BUTTON);
	sort_panel->Bind(wxEVT_RIBBONPANEL_EXTBUTTON_ACTIVATED, &MyTechFrame::OnSortSetting, this);
	sort_bar_ = new wxRibbonButtonBar(sort_panel);
	InnerUpdateSort();
#endif

	wxRibbonPanel *analysis_panel = new wxRibbonPanel(home, wxID_ANY, "技术分析",
		wxNullBitmap, wxDefaultPosition, wxDefaultSize,
		wxRIBBON_PANEL_NO_AUTO_MINIMISE |
		wxRIBBON_PANEL_EXT_BUTTON);
	analysis_panel->Bind(wxEVT_RIBBONPANEL_EXTBUTTON_ACTIVATED, &MyTechFrame::OnTechSetting, this);
	tech_bar_ = new wxRibbonToolBar(analysis_panel);
	InnerUpdateTechBar();

	/*auto template_panel = new wxRibbonPanel(home, wxID_ANY, "模板");
	wxRibbonButtonBar* template_bar = new wxRibbonButtonBar(template_panel);
	template_bar->AddButton(ID_TEMPLATE_SAVE, "保存", skin_info_ptr_->GetBitmap32(wxEmptyString));
	template_bar->SetButtonMaxSizeClass(ID_TEMPLATE_SAVE, wxRIBBON_BUTTONBAR_BUTTON_LARGE);
	template_bar->SetButtonMinSizeClass(ID_TEMPLATE_SAVE, wxRIBBON_BUTTONBAR_BUTTON_LARGE);
	template_bar->AddButton(ID_TEMPLATE_NEW_WINDOW, "新窗口", skin_info_ptr_->GetBitmap32(wxEmptyString));
	template_bar->SetButtonMaxSizeClass(ID_TEMPLATE_NEW_WINDOW, wxRIBBON_BUTTONBAR_BUTTON_LARGE);
	template_bar->SetButtonMinSizeClass(ID_TEMPLATE_NEW_WINDOW, wxRIBBON_BUTTONBAR_BUTTON_LARGE);*/

#ifdef _TRADE

	//脚本
	auto script_panel = new wxRibbonPanel(home, wxID_ANY, wxT("快速脚本"), wxNullBitmap, wxDefaultPosition, wxDefaultSize
		, wxRIBBON_PANEL_NO_AUTO_MINIMISE | wxRIBBON_PANEL_EXT_BUTTON);
	script_panel->Bind(wxEVT_RIBBONPANEL_EXTBUTTON_ACTIVATED, &MyTechFrame::OnScriptSetting, this);
	script_bar_ = new wxRibbonButtonBar(script_panel);
	InnerUpdateScript();

#endif//_TRADE

	/*auto other_panel = new wxRibbonPanel(home, wxID_ANY, "其他", wxNullBitmap, wxDefaultPosition, wxDefaultSize
		, wxRIBBON_PANEL_NO_AUTO_MINIMISE | wxRIBBON_PANEL_EXT_BUTTON);
	wxRibbonButtonBar* other_bar = new wxRibbonButtonBar(other_panel);
	other_bar->AddButton(ID_CLEAR_SETTINGS, "重新设置", skin_info_ptr_->GetBitmap32(wxEmptyString));
	other_bar->SetButtonMaxSizeClass(ID_CLEAR_SETTINGS, wxRIBBON_BUTTONBAR_BUTTON_LARGE);
	other_bar->SetButtonMinSizeClass(ID_CLEAR_SETTINGS, wxRIBBON_BUTTONBAR_BUTTON_LARGE);
	other_bar->AddButton(ID_CLEAR_DATA, "清理数据", skin_info_ptr_->GetBitmap32(wxEmptyString));
	other_bar->SetButtonMaxSizeClass(ID_CLEAR_DATA, wxRIBBON_BUTTONBAR_BUTTON_LARGE);
	other_bar->SetButtonMinSizeClass(ID_CLEAR_DATA, wxRIBBON_BUTTONBAR_BUTTON_LARGE);*/
	//other_bar->AddButton(ID_HELP_ABOUT, "关于", skin_info_ptr_->GetBitmap32(wxEmptyString));
	//other_bar->SetButtonMaxSizeClass(ID_HELP_ABOUT, wxRIBBON_BUTTONBAR_BUTTON_MEDIUM);
	//other_bar->SetButtonMinSizeClass(ID_HELP_ABOUT, wxRIBBON_BUTTONBAR_BUTTON_MEDIUM);
}

void MyTechFrame::AddLastPages()
{
	wxRibbonPage* scheme = new wxRibbonPage(m_ribbon, wxID_ANY, wxT("新窗口"));
	last_pages_[scheme] = [this](wxRibbonBarEvent& evt) {
		wxGetApp().Goto(Get());
	};
}

void MyTechFrame::InnerUpdateNavigateKey(const wxString& key)
{
	auto panel = navigate_bar_->GetParent();
	panel->SetLabel(key.empty() ? wxT("导航/选股：") : key);
	panel->Refresh(false);
}

void MyTechFrame::InnerUpdateNavigate()
{
	navigate_bar_->ClearButtons();

	navigate_bar_->AddHybridButton(ID_NAVIGATE_BACKWARD, wxT("返回"), skin_info_ptr_->GetBitmap32(wxT("返回")), wxT("返回前一个代码"));
	navigate_bar_->SetButtonTextMinWidth(ID_NAVIGATE_BACKWARD, wxT("前一个"));
	navigate_bar_->SetButtonMaxSizeClass(ID_NAVIGATE_BACKWARD, wxRIBBON_BUTTONBAR_BUTTON_LARGE);
	navigate_bar_->SetButtonMinSizeClass(ID_NAVIGATE_BACKWARD, wxRIBBON_BUTTONBAR_BUTTON_LARGE);
	/*navigate_bar_->AddHybridButton(ID_NAVIGATE_FORWARD, wxT("前进"), skin_info_ptr_->GetBitmap32(wxT("前进")));
	//navigate_bar_->SetButtonTextMinWidth(ID_NAVIGATE_FORWARD, wxT("上一个"));
	navigate_bar_->SetButtonMaxSizeClass(ID_NAVIGATE_FORWARD, wxRIBBON_BUTTONBAR_BUTTON_MEDIUM);
	navigate_bar_->SetButtonMinSizeClass(ID_NAVIGATE_FORWARD, wxRIBBON_BUTTONBAR_BUTTON_MEDIUM);
	navigate_bar_->AddHybridButton(ID_NAVIGATE_FIND, "搜索", skin_info_ptr_->GetBitmap32(wxT("搜索")));
	//navigate_bar_->SetButtonTextMinWidth(ID_NAVIGATE_FIND, wxT("上一个"));
	navigate_bar_->SetButtonMaxSizeClass(ID_NAVIGATE_FIND, wxRIBBON_BUTTONBAR_BUTTON_LARGE);
	navigate_bar_->SetButtonMinSizeClass(ID_NAVIGATE_FIND, wxRIBBON_BUTTONBAR_BUTTON_LARGE);*/
	/*navigate_bar_->AddButton(ID_NAVIGATE_UP, "上翻"
	, wxArtProvider::GetBitmap(wxART_GO_UP, wxART_OTHER, wxSize(16, 16))
	, "上一个代码");
	navigate_bar_->SetButtonMaxSizeClass(ID_NAVIGATE_UP, wxRIBBON_BUTTONBAR_BUTTON_MEDIUM);
	navigate_bar_->SetButtonMinSizeClass(ID_NAVIGATE_UP, wxRIBBON_BUTTONBAR_BUTTON_MEDIUM);
	navigate_bar_->AddButton(ID_NAVIGATE_DOWN, "下翻"
	, wxArtProvider::GetBitmap(wxART_GO_DOWN, wxART_OTHER, wxSize(16, 16))
	, "下一个代码");
	navigate_bar_->SetButtonMaxSizeClass(ID_NAVIGATE_DOWN, wxRIBBON_BUTTONBAR_BUTTON_MEDIUM);
	navigate_bar_->SetButtonMinSizeClass(ID_NAVIGATE_DOWN, wxRIBBON_BUTTONBAR_BUTTON_MEDIUM);*/
	//navigate_bar_->AddHybridButton(ID_NAVIGATE_ALL, wxT(SMARTKB_KEY_ALL), skin_info_ptr_->GetBitmap32(wxT("全部")));
	//	//, wxArtProvider::GetBitmap(wxART_GO_HOME, wxART_OTHER, wxSize(32, 32)));
	//navigate_bar_->SetButtonMaxSizeClass(ID_NAVIGATE_ALL, wxRIBBON_BUTTONBAR_BUTTON_LARGE);
	//navigate_bar_->SetButtonMinSizeClass(ID_NAVIGATE_ALL, wxRIBBON_BUTTONBAR_BUTTON_LARGE);
	//navigate_bar_->AddButton(ID_NAVIGATE_NEW, "新窗口", skin_info_ptr_->GetBitmap32(wxEmptyString));
	//navigate_bar_->SetButtonMaxSizeClass(ID_NAVIGATE_NEW, wxRIBBON_BUTTONBAR_BUTTON_LARGE);
	//navigate_bar_->SetButtonMinSizeClass(ID_NAVIGATE_NEW, wxRIBBON_BUTTONBAR_BUTTON_LARGE);
	//
	navigate_bar_->AddButton(ID_MARKET_SUBSCRIBE, wxT("订阅"), skin_info_ptr_->GetBitmap32(wxT("订阅")), wxT("查看已订阅代码"));
	//, wxArtProvider::GetBitmap(wxART_GO_HOME, wxART_OTHER, wxSize(32, 32)));
	navigate_bar_->SetButtonMaxSizeClass(ID_MARKET_SUBSCRIBE, wxRIBBON_BUTTONBAR_BUTTON_LARGE);
	navigate_bar_->SetButtonMinSizeClass(ID_MARKET_SUBSCRIBE, wxRIBBON_BUTTONBAR_BUTTON_LARGE);
	navigate_bar_->AddHybridButton(ID_MARKET_SELF, wxT(SMARTKB_KEY_SELFSEL), skin_info_ptr_->GetBitmap32(wxT(SMARTKB_KEY_SELFSEL)), wxT("查看自选代码"));
	//, wxArtProvider::GetBitmap(wxART_GO_HOME, wxART_OTHER, wxSize(32, 32)));
	navigate_bar_->SetButtonMaxSizeClass(ID_MARKET_SELF, wxRIBBON_BUTTONBAR_BUTTON_LARGE);
	navigate_bar_->SetButtonMinSizeClass(ID_MARKET_SELF, wxRIBBON_BUTTONBAR_BUTTON_LARGE);
	navigate_bar_->AddHybridButton(ID_MARKET_ALL, wxT("全部"), skin_info_ptr_->GetBitmap32(wxT("市场")), wxT("查看全部代码"));
	navigate_bar_->SetButtonMaxSizeClass(ID_MARKET_ALL, wxRIBBON_BUTTONBAR_BUTTON_LARGE);
	navigate_bar_->SetButtonMinSizeClass(ID_MARKET_ALL, wxRIBBON_BUTTONBAR_BUTTON_LARGE);
	/*const char* cats[] = { u8"股票"
		, u8"债券"
		, u8"基金"
		, u8"期货"
	};
	for (auto str : cats)
	{
		for (size_t i = 0, j = all_category_.size(); i < j; ++i)
		{
			auto name = ZQDBGetCategoryName(all_category_[i]);
			if (strcmp(name, str) == 0) {
				InnerUpdateOneCategory(i);
			}
		}
	}*/
	//
	if (all_container_func_.size()) {
		//cur_container_func_ = 0;
		zqdb::Calc::Func func(all_container_func_[cur_container_func_]);
		char buf[260] = { 0 };
		auto name = func.GetAttrAsStr("name", buf, 260);
		wxString label = utf2wxString(name[0] ? name : func.GetCalcName());
		navigate_bar_->AddHybridButton(ID_CONTAINER + cur_container_func_, label, skin_info_ptr_->GetBitmap32(wxT("筛选")));
		navigate_bar_->SetButtonMaxSizeClass(ID_CONTAINER + cur_container_func_, wxRIBBON_BUTTONBAR_BUTTON_LARGE);
		navigate_bar_->SetButtonMinSizeClass(ID_CONTAINER + cur_container_func_, wxRIBBON_BUTTONBAR_BUTTON_LARGE);
	}

	navigate_bar_->AddDropdownButton(ID_NAVIGATE_FIND, wxT("云选股"), skin_info_ptr_->GetBitmap32(wxT("搜索")));
	navigate_bar_->SetButtonMaxSizeClass(ID_NAVIGATE_FIND, wxRIBBON_BUTTONBAR_BUTTON_LARGE);
	navigate_bar_->SetButtonMinSizeClass(ID_NAVIGATE_FIND, wxRIBBON_BUTTONBAR_BUTTON_LARGE);
	for (size_t i = 0, j = all_filter_func_.size(); i < j; ++i)
	{
		zqdb::Calc::Func func(all_filter_func_[i]);
		char buf[260] = { 0 };
		auto name = func.GetAttrAsStr("name", buf, 260);
		wxString label = utf2wxString(name[0] ? name : func.GetCalcName());
		navigate_bar_->AddHybridButton(ID_FILTER + i, label, skin_info_ptr_->GetBitmap32(wxT("筛选")));
		navigate_bar_->SetButtonMaxSizeClass(ID_FILTER + i, wxRIBBON_BUTTONBAR_BUTTON_LARGE);
		navigate_bar_->SetButtonMinSizeClass(ID_FILTER + i, wxRIBBON_BUTTONBAR_BUTTON_LARGE);
	}
}

void MyTechFrame::InnerUpdateOneCategory(size_t pos)
{
	zqdb::Category cat(all_category_[pos]);
	if (cat) {
		auto name = utf2wxString(cat.Name());
		auto item = navigate_bar_->AddHybridButton(ID_MARKET_CATEGORY + pos, name, skin_info_ptr_->GetBitmap32(name));
		navigate_bar_->SetItemClientData(item, (HZQDB)cat);
		navigate_bar_->SetButtonMaxSizeClass(ID_MARKET_CATEGORY + pos, wxRIBBON_BUTTONBAR_BUTTON_LARGE);
		navigate_bar_->SetButtonMinSizeClass(ID_MARKET_CATEGORY + pos, wxRIBBON_BUTTONBAR_BUTTON_LARGE);
	}
}

void MyTechFrame::InnerUpdateOneContainer(size_t pos)
{
	zqdb::Calc::Func func(all_container_func_[pos]);
	char buf[260] = { 0 };
	auto name = func.GetAttrAsStr("name", buf, 260);
	wxString label = utf2wxString(name[0] ? name : func.GetCalcName());
	navigate_bar_->AddHybridButton(ID_CONTAINER + pos, label, skin_info_ptr_->GetBitmap32(wxT("筛选")));
	navigate_bar_->SetButtonMaxSizeClass(ID_CONTAINER + pos, wxRIBBON_BUTTONBAR_BUTTON_LARGE);
	navigate_bar_->SetButtonMinSizeClass(ID_CONTAINER + pos, wxRIBBON_BUTTONBAR_BUTTON_LARGE);
}

void MyTechFrame::InnerSetCurContainer(size_t cur)
{
	ASSERT(cur_container_func_ < all_container_func_.size());
	navigate_bar_->DeleteButton(ID_CONTAINER + cur_container_func_);
	cur_container_func_ = cur;
	ASSERT(cur_container_func_ < all_container_func_.size());
	zqdb::Calc::Func func(all_container_func_[cur_container_func_]);
	char buf[260] = { 0 };
	auto name = func.GetAttrAsStr("name", buf, 260);
	wxString label = utf2wxString(name[0] ? name : func.GetCalcName());
	navigate_bar_->AddHybridButton(ID_CONTAINER + cur_container_func_, label, skin_info_ptr_->GetBitmap32(wxT("筛选")));
	navigate_bar_->SetButtonMaxSizeClass(ID_CONTAINER + cur_container_func_, wxRIBBON_BUTTONBAR_BUTTON_LARGE);
	navigate_bar_->SetButtonMinSizeClass(ID_CONTAINER + cur_container_func_, wxRIBBON_BUTTONBAR_BUTTON_LARGE);
}

//void MyTechFrame::InnerUpdateFilter()
//{
//	filter_bar_->ClearButtons();
//	//
//#if 1
//	filter_bar_->AddButton(ID_FILTER_MAX, wxT("搜索"), skin_info_ptr_->GetBitmap32(wxT("搜索")));
//	filter_bar_->SetButtonMaxSizeClass(ID_FILTER_MAX, wxRIBBON_BUTTONBAR_BUTTON_LARGE);
//	filter_bar_->SetButtonMinSizeClass(ID_FILTER_MAX, wxRIBBON_BUTTONBAR_BUTTON_LARGE);
//	for (size_t i = 0, j = all_filter_func_.size(); i < j; ++i)
//	{
//		InnerUpdateOneFilter(i);
//	}
//#else
//	std::set<size_t> uses;
//	for (size_t i = 0, j = all_filter_func_.size(); i < j; ++i)
//	{
//		zqdb::Calc::Func func(all_filter_func_[i]);
//		if (strcmp(func.GetCalcName(), "cse") == 0) {
//			InnerUpdateOneFilter(i);
//			uses.insert(i);
//		}
//	}
//	for (size_t i = 0, j = all_filter_func_.size(); i < j; ++i)
//	{
//		zqdb::Calc::Func func(all_filter_func_[i]);
//		if (strcmp(func.GetCalcName(), "cfe") == 0) {
//			InnerUpdateOneFilter(i);
//			uses.insert(i);
//		}
//	}
//	for (size_t i = 0, j = all_filter_func_.size(); i < j; ++i)
//	{
//		zqdb::Calc::Func func(all_filter_func_[i]);
//		if (strcmp(func.GetCalcName(), "cse") != 0 
//			&& strcmp(func.GetCalcName(), "cfe") != 0) {
//			if (func.HasAttr("menu")) {
//				InnerUpdateOneFilter(i);
//				uses.insert(i);
//			}
//		}
//	}
//	if (all_filter_func_.size() > uses.size()) {
//		if (uses.find(cur_filter_func_) != uses.end()) {
//			for (cur_filter_func_ = 0; cur_filter_func_ < all_filter_func_.size(); cur_filter_func_++)
//			{
//				if (uses.find(cur_filter_func_) == uses.end()) {
//					break;
//				}
//			}
//		}
//		zqdb::Calc::Func func(all_filter_func_[cur_filter_func_]);
//		char buf[260] = { 0 };
//		auto name = func.GetAttrAsStr("name", buf, 260);
//		wxString label = utf2wxString(name[0] ? name : func.GetCalcName());
//		if ((all_filter_func_.size() - uses.size()) <= 1) {
//			filter_bar_->AddButton(ID_FILTER + cur_filter_func_, label, skin_info_ptr_->GetBitmap32(wxT("筛选")));
//		}
//		else {
//			filter_bar_->AddHybridButton(ID_FILTER + cur_filter_func_, label, skin_info_ptr_->GetBitmap32(wxT("筛选")));
//		}
//		filter_bar_->SetButtonMaxSizeClass(ID_FILTER + cur_filter_func_, wxRIBBON_BUTTONBAR_BUTTON_LARGE);
//		filter_bar_->SetButtonMinSizeClass(ID_FILTER + cur_filter_func_, wxRIBBON_BUTTONBAR_BUTTON_LARGE);
//	}
//#endif
//}
//
//void MyTechFrame::InnerUpdateOneFilter(size_t pos)
//{
//	zqdb::Calc::Func func(all_filter_func_[pos]);
//	char buf[260] = { 0 };
//	auto name = func.GetAttrAsStr("name", buf, 260);
//	wxString label = utf2wxString(name[0] ? name : func.GetCalcName());
//	filter_bar_->AddHybridButton(ID_FILTER + pos, label, skin_info_ptr_->GetBitmap32(wxT("筛选")));
//	filter_bar_->SetButtonMaxSizeClass(ID_FILTER + pos, wxRIBBON_BUTTONBAR_BUTTON_LARGE);
//	filter_bar_->SetButtonMinSizeClass(ID_FILTER + pos, wxRIBBON_BUTTONBAR_BUTTON_LARGE);
//}

#ifdef _SORT
void MyTechFrame::InnerUpdateSort()
{
	sort_bar_->ClearButtons();
	sort_bar_->AddButton(ID_SORT, wxT("涨跌幅"), skin_info_ptr_->GetBitmap32(wxT("涨跌幅")));
	//, wxArtProvider::GetBitmap(wxART_GO_UP, wxART_OTHER, wxSize(32, 32)));
	sort_bar_->SetButtonMaxSizeClass(ID_SORT, wxRIBBON_BUTTONBAR_BUTTON_LARGE);
	sort_bar_->SetButtonMinSizeClass(ID_SORT, wxRIBBON_BUTTONBAR_BUTTON_LARGE);
	/*sort_bar_->AddHybridButton(ID_SORT_QUICK
		, wxString::Format(wxT("%zu分钟涨跌"), wxGetApp().GetSortQuick() / 60), skin_info_ptr_->GetBitmap32(wxT("涨跌速")));
	//, wxArtProvider::GetBitmap(wxART_GO_UP, wxART_OTHER, wxSize(32, 32)));
	sort_bar_->SetButtonMaxSizeClass(ID_SORT_QUICK, wxRIBBON_BUTTONBAR_BUTTON_LARGE);
	sort_bar_->SetButtonMinSizeClass(ID_SORT_QUICK, wxRIBBON_BUTTONBAR_BUTTON_LARGE);*/
	if (!all_sort_func_.empty()) {
		ASSERT(cur_sort_func_ < all_sort_func_.size());
		zqdb::Calc::Func func(all_sort_func_[cur_sort_func_]);
		char buf[260] = { 0 };
		auto name = func.GetAttrAsStr("name", buf, 260);
		wxString label = utf2wxString(name[0] ? name : func.GetCalcName());
		if (all_sort_func_.size() <= 1) {
			sort_bar_->AddButton(ID_SORT_CALC + cur_sort_func_, label, skin_info_ptr_->GetBitmap32(wxT("排序")));
		}
		else {
			sort_bar_->AddHybridButton(ID_SORT_CALC + cur_sort_func_, label, skin_info_ptr_->GetBitmap32(wxT("排序")));
		}
		sort_bar_->SetButtonMaxSizeClass(ID_SORT_CALC + cur_sort_func_, wxRIBBON_BUTTONBAR_BUTTON_LARGE);
		sort_bar_->SetButtonMinSizeClass(ID_SORT_CALC + cur_sort_func_, wxRIBBON_BUTTONBAR_BUTTON_LARGE);
	}
	MY_CODE_SORT_TYPE type;
	size_t secs = 0;
	int sort = IsSort(&type, &secs);
	if (sort) {
		switch (type)
		{
		case SORT_ZDF:
			sort_bar_->SetButtonText(ID_SORT, sort > 0 ? wxT("涨跌幅▼") : wxT("涨跌幅▲"));
			break;
		case SORT_ZDS:
			sort_bar_->SetButtonText(ID_SORT_QUICK
				, wxString::Format(wxT("%zu分钟涨跌%s"), secs / 60, sort > 0 ? wxT("▼") : wxT("▲")));
			break;
		case SORT_CALC_SORT: {
			zqdb::Calc::Func func(all_sort_func_[cur_sort_func_]);
			char buf[260] = { 0 };
			auto name = func.GetAttrAsStr("name", buf, 260);
			auto calc_name = func.GetCalcName();
			wxString label = utf2wxString(name[0] ? name : calc_name);
			sort_bar_->SetButtonText(ID_SORT_CALC + cur_sort_func_, sort > 0 ? label + wxT("▼") : label + wxT("▲"));
		} break;
		}
	}
}

void MyTechFrame::InnerSetCurSort(size_t cur)
{
	ASSERT(cur_sort_func_ < all_sort_func_.size());
	sort_bar_->DeleteButton(ID_SORT_CALC + cur_sort_func_);
	cur_sort_func_ = cur;
	ASSERT(cur_sort_func_ < all_sort_func_.size());
	zqdb::Calc::Func func(all_sort_func_[cur_sort_func_]);
	char buf[260] = { 0 };
	auto name = func.GetAttrAsStr("name", buf, 260);
	auto calc_name = func.GetCalcName();
	wxString label = utf2wxString(name[0] ? name : calc_name);
	sort_bar_->AddHybridButton(ID_SORT_CALC + cur_sort_func_, label, skin_info_ptr_->GetBitmap32(wxT("指标排序")));
	sort_bar_->SetButtonMaxSizeClass(ID_SORT_CALC + cur_sort_func_, wxRIBBON_BUTTONBAR_BUTTON_LARGE);
	sort_bar_->SetButtonMinSizeClass(ID_SORT_CALC + cur_sort_func_, wxRIBBON_BUTTONBAR_BUTTON_LARGE);
	wxGetApp().SetSortCalcFunc(calc_name);
}
#endif

void MyTechFrame::InnerUpdateTechBar()
{
	//线型
	tech_bar_->AddToggleTool(ID_TECHVIEW_KLINE + CALC_KLINE_K, skin_info_ptr_->GetBitmap16(wxT("K线")), wxT("K线"));
	tech_bar_->AddToggleTool(ID_TECHVIEW_KLINE + CALC_KLINE_BAR, skin_info_ptr_->GetBitmap16(wxT("美国线")), wxT("美国线"));
	tech_bar_->AddToggleTool(ID_TECHVIEW_KLINE + CALC_KLINE_TREND, skin_info_ptr_->GetBitmap16(wxT("趋势线")), wxT("趋势线"));
	tech_bar_->AddSeparator();
	tech_bar_->ToggleTool(ID_TECHVIEW_KLINE + wxGetApp().GetTechKType(), true);
	//周期
	tech_bar_->AddToggleTool(ID_TECHVIEW_CYCLE + CYC_TICK, skin_info_ptr_->GetBitmap16(wxT("TICK")), wxT("显示分时走势"));
	for (int cycle = 0; cycle < CYC_MAX; cycle++)
	{
		bool add = cycle == wxGetApp().GetTechCycleCur();
		if (!add) {
			switch (cycle)
			{
			case CYC_TICK:
#if USE_CYC_SEC
			case CYC_ANYSEC:
#endif
				//case CYC_15MIN:
				//case CYC_30MIN:
			case CYC_ANYMIN:
			case CYC_WEEK:
			case CYC_MONTH:
			case CYC_SEASON:
			case CYC_YEAR:
				break;
			default: {
				add = true;
			} break;
			}
		}
		if (add) {
			wxString str = ZQDBCycle2Str((PERIODTYPE)cycle);
			tech_bar_->AddToggleTool(ID_TECHVIEW_CYCLE + cycle, skin_info_ptr_->GetBitmap16(str));
		}
	}
	tech_bar_->AddDropdownTool(ID_TECHVIEW_CYCLE + CYC_MAX, skin_info_ptr_->GetBitmap16(wxT("更多"))); //all
	tech_bar_->AddSeparator();
	tech_bar_->ToggleTool(ID_TECHVIEW_CYCLE + wxGetApp().GetTechCycleCur(), true);
	//划线
	tech_bar_->AddTool(ID_TECHVIEW_DRAWLINE, skin_info_ptr_->GetBitmap16(wxT("光标")), wxT("光标")); //恢复
	tech_bar_->AddTool(ID_TECHVIEW_DRAWLINE + CALC_DRAWLINE_DDLINE, skin_info_ptr_->GetBitmap16(wxT("直线")), wxT("直线"));
	tech_bar_->AddTool(ID_TECHVIEW_DRAWLINE + CALC_DRAWLINE_DOTDOT, skin_info_ptr_->GetBitmap16(wxT("线段")), wxT("线段"));
	tech_bar_->AddTool(ID_TECHVIEW_DRAWLINE + CALC_DRAWLINE_PLINE, skin_info_ptr_->GetBitmap16(wxT("平行线")), wxT("平行线"));
	tech_bar_->AddTool(ID_TECHVIEW_DRAWLINE + CALC_DRAWLINE_REGRESSCHANNEL, skin_info_ptr_->GetBitmap16(wxT("通道线")), wxT("通道线"));
	tech_bar_->AddTool(ID_TECHVIEW_DRAWLINE + CALC_DRAWLINE_CYCLELINE, skin_info_ptr_->GetBitmap16(wxT("周期线")), wxT("周期线"));
	tech_bar_->AddTool(ID_TECHVIEW_DRAWLINE + CALC_DRAWLINE_FABCYCLELINE, skin_info_ptr_->GetBitmap16(wxT("斐波那契周期线")), wxT("斐波那契周期线"));
	tech_bar_->AddTool(ID_TECHVIEW_DRAWLINE + CALC_DRAWLINE_GOLDLINE, skin_info_ptr_->GetBitmap16(wxT("黄金分割线")), wxT("黄金分割线"));
	//tech_bar_->AddTool(ID_TECHVIEW_DRAWLINE + CALC_DRAWLINE_TEXT, skin_info_ptr_->GetBitmap16(wxT("文字")));
	//tech_bar_->AddTool(ID_TECHVIEW_DRAWLINE + CALC_DRAWLINE_UPARROW, skin_info_ptr_->GetBitmap16(wxT("上箭头")));
	//tech_bar_->AddTool(ID_TECHVIEW_DRAWLINE + CALC_DRAWLINE_DOWNARROW, skin_info_ptr_->GetBitmap16(wxT("下箭头")));
	tech_bar_->AddDropdownTool(ID_TECHVIEW_DRAWLINE_MAX, skin_info_ptr_->GetBitmap16(wxT("更多"))); //All
	tech_bar_->AddSeparator();
	//缩放
	tech_bar_->AddTool(ID_TECHVIEW_ZOOM_IN, skin_info_ptr_->GetBitmap16(wxT("缩小")), wxT("缩小K线"));
	tech_bar_->AddTool(ID_TECHVIEW_ZOOM_OUT, skin_info_ptr_->GetBitmap16(wxT("放大")), wxT("放大K线"));
	tech_bar_->AddSeparator();
	//移动
	//tech_bar_->AddToggleTool(ID_TECHVIEW_MOVE_AUTO, skin_info_ptr_->GetBitmap16(wxT("自动滚动")));
	//tech_bar_->AddToggleTool(ID_TECHVIEW_MOVE_SHIFT, skin_info_ptr_->GetBitmap16(wxT("转移图表")));
	//tech_bar_->AddTool(wxID_ANY, wxArtProvider::GetBitmap(wxART_GOTO_FIRST, wxART_OTHER, wxSize(16, 15)));
	//tech_bar_->AddToggleTool(wxID_ANY, wxArtProvider::GetBitmap(wxART_GO_FORWARD, wxART_OTHER, wxSize(16, 15)));
	//tech_bar_->AddTool(wxID_ANY, wxArtProvider::GetBitmap(wxART_GO_BACK, wxART_OTHER, wxSize(16, 15)));
	//tech_bar_->AddToggleTool(wxID_ANY, wxArtProvider::GetBitmap(wxART_GOTO_LAST, wxART_OTHER, wxSize(16, 15)));
	//tech_bar_->AddSeparator();//缩放
	tech_bar_->AddDropdownTool(ID_TECHVIEW_VIEW, skin_info_ptr_->GetBitmap16(wxT("3图")), wxT("视图组合"));
	tech_bar_->AddSeparator();
	tech_bar_->AddTool(ID_INFO_F10, skin_info_ptr_->GetBitmap16(wxT("F10")), wxT("F10"));
	tech_bar_->AddSeparator();
	/*//加减指标
	tech_bar_->AddDropdownTool(wxID_ADD, wxArtProvider::GetBitmap(wxART_PLUS, wxART_OTHER, wxSize(16, 15)));
	tech_bar_->AddDropdownTool(wxID_REMOVE, wxArtProvider::GetBitmap(wxART_MINUS, wxART_OTHER, wxSize(16, 15)));
	tech_bar_->AddSeparator();*/
	/*//技术模板
	tech_bar_->AddHybridTool(wxID_NEW, wxArtProvider::GetBitmap(wxART_NEW, wxART_OTHER, wxSize(16, 15)));
	tech_bar_->AddTool(wxID_OPEN, wxArtProvider::GetBitmap(wxART_FILE_OPEN, wxART_OTHER, wxSize(16, 15)), "Open something");
	tech_bar_->AddTool(wxID_SAVE, wxArtProvider::GetBitmap(wxART_FILE_SAVE, wxART_OTHER, wxSize(16, 15)), "Save something");
	tech_bar_->AddTool(wxID_SAVEAS, wxArtProvider::GetBitmap(wxART_FILE_SAVE_AS, wxART_OTHER, wxSize(16, 15)), "Save something as ...");
	tech_bar_->EnableTool(wxID_OPEN, false);
	tech_bar_->EnableTool(wxID_SAVE, false);
	tech_bar_->EnableTool(wxID_SAVEAS, false);
	tech_bar_->AddSeparator();*/
	//
	tech_bar_->SetRows(3, 3);
}

void MyTechFrame::InnerUpdateScript()
{
	script_bar_->ClearButtons();
	/*script_bar->AddHybridButton(ID_SCRIPT_BEGIN, "快买", skin_info_ptr_->GetBitmap32(wxEmptyString));
	script_bar->SetButtonMaxSizeClass(ID_SCRIPT_BEGIN, wxRIBBON_BUTTONBAR_BUTTON_LARGE);
	script_bar->SetButtonMinSizeClass(ID_SCRIPT_BEGIN, wxRIBBON_BUTTONBAR_BUTTON_LARGE);
	script_bar->AddHybridButton(ID_SCRIPT_BEGIN + 1, "快卖", skin_info_ptr_->GetBitmap32(wxEmptyString));
	script_bar->SetButtonMaxSizeClass(ID_SCRIPT_BEGIN + 1, wxRIBBON_BUTTONBAR_BUTTON_LARGE);
	script_bar->SetButtonMinSizeClass(ID_SCRIPT_BEGIN + 1, wxRIBBON_BUTTONBAR_BUTTON_LARGE);
	script_bar->AddHybridButton(ID_SCRIPT_BEGIN + 2, "快平", skin_info_ptr_->GetBitmap32(wxEmptyString));
	script_bar->SetButtonMaxSizeClass(ID_SCRIPT_BEGIN + 2, wxRIBBON_BUTTONBAR_BUTTON_LARGE);
	script_bar->SetButtonMinSizeClass(ID_SCRIPT_BEGIN + 2, wxRIBBON_BUTTONBAR_BUTTON_LARGE);*/
	bool test = false;
	std::set<HZQDB> uses;
	for (size_t i = 0, j = all_script_func_.size(); i < j; ++i)
	{
		zqdb::Calc::Func func(all_script_func_[i]);
		if (strcmp(func.GetCalcName(), "export") == 0) {
			InnerUpdateOneScript(i);
			uses.insert(all_script_func_[i]);
		}
	}
	for (size_t i = 0, j = all_script_func_.size(); i < j; ++i)
	{
		zqdb::Calc::Func func(all_script_func_[i]);
		if (strcmp(func.GetCalcName(), "export") != 0) {
			if (func.HasAttr("menu")) {
				InnerUpdateOneScript(i);
				uses.insert(all_script_func_[i]);
			}
		}
	}
	if (all_script_func_.size() > uses.size()) {
		for (size_t i = 0, j = all_script_func_.size(); i < j; i++)
		{
			if (uses.find(all_script_func_[i]) != uses.end()) {
				continue;
			}
			zqdb::Calc::Func func(all_script_func_[i]);
			char buf[260] = { 0 };
			auto type = func.GetAttrAsStr("type", buf, 260);
			if (strcmp(type, "test") == 0) {
				test = true;
				continue;
			}
			InnerUpdateOneScript(i);
		}
	}
	if (test) {
		for (size_t i = 0, j = all_script_func_.size(); i < j; i++)
		{
			if (uses.find(all_script_func_[i]) != uses.end()) {
				continue;
			}
			zqdb::Calc::Func func(all_script_func_[i]);
			char buf[260] = { 0 };
			auto type = func.GetAttrAsStr("type", buf, 260);
			if (strcmp(type, "test") != 0) {
				continue;
			}
			InnerUpdateOneScript(i);
		}
	}
}

void MyTechFrame::InnerUpdateOneScript(size_t pos)
{
	zqdb::Calc::Func func(all_script_func_[pos]);
	char buf[260] = { 0 };
	auto name = func.GetAttrAsStr("name", buf, 260);
	wxString label = utf2wxString(name[0] ? name : func.GetCalcName());
	script_bar_->AddHybridButton(ID_SCRIPT + pos, label, skin_info_ptr_->GetBitmap32(wxT("脚本")));
	script_bar_->SetButtonMaxSizeClass(ID_SCRIPT + pos, wxRIBBON_BUTTONBAR_BUTTON_LARGE);
	script_bar_->SetButtonMinSizeClass(ID_SCRIPT + pos, wxRIBBON_BUTTONBAR_BUTTON_LARGE);
}

int MyTechFrame::FilterEvent(wxEvent& event)
{
#if 1
	const wxEventType t = event.GetEventType();
	auto obj = event.GetEventObject();
	if (obj) {
		if (t == wxEVT_KEY_DOWN) {
			if (!wxDynamicCast(obj, wxTextCtrl) || wxDynamicCast(obj, wxRichTextCtrl) || wxDynamicCast(obj, wxStyledTextCtrl)) {
				auto key_event = wxDynamicCast(&event, wxKeyEvent);
				if (!key_event->HasAnyModifiers()) {
					auto key_code = key_event->GetKeyCode();
					switch (key_code)
					{
					case WXK_F10: {
						ShowF10();
						return wxEventFilter::Event_Ignore;
					} break;
					default: {
						if (isalnum(key_code)) {
							/*if (code_view_->IsFilter()) {
								wxMessageBox(wxT("筛选中...，不支持搜索，请先取消筛选！"), wxT("提示"));
							}
							else*/ {
								wxGetApp().GetSmartKBDlg()->ShowFor(this, key_code);
								return wxEventFilter::Event_Ignore;
							}
						}
					} break;
					}
				}
			}
		}
	}
#endif

	if (code_view_) {
		int rlt = code_view_->FilterEvent(event);
		if (wxEventFilter::Event_Skip != rlt) {
			return rlt;
		}
	}

	//const wxEventType t = event.GetEventType();
	//auto obj = event.GetEventObject();
	//if (obj == tech_view_) {
	//	if (t == wxEVT_MOUSEWHEEL) {
	//		auto mouse_event = wxDynamicCast(&event, wxMouseEvent);
	//		if (!tech_view_->IsAnyOperator()) {
	//			auto rotation = mouse_event->GetWheelRotation();
	//			auto delta = mouse_event->GetWheelDelta();
	//			auto inverted = mouse_event->IsWheelInverted();
	//			auto axis = mouse_event->GetWheelAxis();
	//			if (axis == wxMOUSE_WHEEL_VERTICAL) {
	//				if (rotation < 0) {
	//					Up();
	//					return wxEventFilter::Event_Ignore;
	//				}
	//				else if (rotation > 0) {
	//					Down();
	//					return wxEventFilter::Event_Ignore;
	//				}
	//			}
	//		}
	//	}
	//	else if (t == wxEVT_CHAR) {
	//		if (obj->IsKindOf(wxCLASSINFO(wxTextCtrlBase))) {
	//			
	//		}
	//		else {
	//			auto key_event = wxDynamicCast(&event, wxKeyEvent);
	//			auto key_code = key_event->GetKeyCode();
	//			switch (key_code)
	//			{
	//			/*case WXK_ESCAPE: {
	//				auto tv = (TechView*)GetParent();
	//				if (IsShowCrossCursor(disp_info_ptr_->show_cross_cursor)) {
	//					tv->HideCurrent();
	//					event.Skip();
	//				}
	//			} break;
	//			case WXK_HOME: {
	//				auto tv = (TechView*)GetParent();
	//				auto nBarCount = GetBarCount();
	//				tv->MoveTo(std::max<int>(0, nBarCount - (disp_info_ptr_->MaxCount - disp_info_ptr_->Shift)));
	//				if (IsShowCrossCursor(disp_info_ptr_->show_cross_cursor)) {
	//					tv->MoveCurrentTo(nBarCount - 1);
	//					event.Skip();
	//				}
	//			} break;
	//			case WXK_END: {
	//				auto tv = (TechView*)GetParent();
	//				tv->MoveTo(0);
	//				if (IsShowCrossCursor(disp_info_ptr_->show_cross_cursor)) {
	//					tv->MoveCurrentTo(0);
	//					event.Skip();
	//				}
	//			} break;*/
	//			case WXK_PAGEUP: {
	//				Up();
	//				return wxEventFilter::Event_Ignore;
	//			} break;
	//			case WXK_PAGEDOWN: {
	//				Down();
	//				return wxEventFilter::Event_Ignore;
	//			} break;
	//			//case WXK_LEFT: {
	//			//	auto tv = (TechView*)GetParent();
	//			//	if (!IsShowCrossCursor(disp_info_ptr_->show_cross_cursor)) {
	//			//		tv->MoveCurrentTo(disp_info_ptr_->Start);
	//			//	}
	//			//	else {
	//			//		/*if (disp_info_ptr_->Start <= 0 && disp_info_ptr_->Current <= 0) {
	//			//		RequestMore();
	//			//		}*/
	//			//		tv->MoveCurrent(-1);
	//			//	}
	//			//	event.Skip();
	//			//} break;
	//			//case WXK_RIGHT: {
	//			//	auto tv = (TechView*)GetParent();
	//			//	if (!IsShowCrossCursor(disp_info_ptr_->show_cross_cursor)) {
	//			//		tv->MoveCurrentTo(disp_info_ptr_->Start + disp_info_ptr_->Count - 1);
	//			//	}
	//			//	else {
	//			//		tv->MoveCurrent(1);
	//			//	}
	//			//	event.Skip();
	//			//} break;
	//			//case WXK_UP: {
	//			//	auto tv = (TechView*)GetParent();
	//			//	tv->Scale(1);
	//			//	tv->UpdateCurrent();
	//			//	event.Skip();
	//			//} break;
	//			//case WXK_DOWN: {
	//			//	auto tv = (TechView*)GetParent();
	//			//	tv->Scale(-1);
	//			//	tv->UpdateCurrent();
	//			//	event.Skip();
	//			//} break;
	//			default: {
	//				/*if (std::isalnum(key_code)) {
	//					auto dlg = wxGetApp().GetSmartKBDlg();
	//					if (dlg) {
	//						dlg->ShowFor(this, key_code);
	//						return wxEventFilter::Event_Ignore;
	//					}
	//				}*/
	//			} break;
	//			}
	//		}
	//	}
	//}
	// Continue processing the event normally as well.
	return wxEventFilter::Event_Skip;
}

void MyTechFrame::OnSkinInfoChanged()
{
	Freeze();
	//普通显示信息变化
	SetBackgroundColour(skin_info_ptr_->crXYLine);
	//SetBackgroundColour(skin_info_ptr_->crPrimary);
	SetForegroundColour(skin_info_ptr_->crTertiary);
	m_ribbon->ClearPages();
	m_ribbon->SetForegroundColour(skin_info_ptr_->crTertiary);
	m_ribbon->SetArtProvider(skin_info_ptr_->artProvider->Clone());
	//m_ribbon->GetArtProvider()->SetColourScheme(skin_info_ptr_->crPrimary
	//	, skin_info_ptr_->crSecondary, skin_info_ptr_->crTertiary);
	//
	if (code_view_) {
		code_view_->SetSkinInfo(skin_info_ptr_);
	}
	if (tech_view_) {
		tech_view_->SetSkinInfo(skin_info_ptr_);
	}
	if (title_view_) {
		title_view_->SetSkinInfo(skin_info_ptr_);
	}
	if (mmp_view_) {   
		mmp_view_->SetSkinInfo(skin_info_ptr_);
	}
	if (trade_view_) {
		trade_view_->SetSkinInfo(skin_info_ptr_);
	}
	if (info_view_) {
		info_view_->SetSkinInfo(skin_info_ptr_);
	}
	if (tick_view_) {
		tick_view_->SetSkinInfo(skin_info_ptr_);
	}
	if (user_view_) {
		user_view_->SetSkinInfo(skin_info_ptr_);
	}
	//m_togglePanels->SetBackgroundColour(skin_info_ptr_->crXYLine);
	/*Broadcast(this, [this](wxWindow* child) {
		if (child != tech_view_ && child != m_ribbon) {
			Broadcast(child, [this](wxWindow* child) {
				child->SetBackgroundColour(skin_info_ptr_->crBackgnd);
			}, true);
			child->SetBackgroundColour(skin_info_ptr_->crXYLine);
		}
	});*/
	if (status_bar_) {
		status_bar_->SetSkinInfo(skin_info_ptr_);
	}
	//
	UpdateAllPages();
	//
	Refresh();
	Layout();
	Thaw();
}

void MyTechFrame::OnInfoChanged()
{
	HZQDB h = Get();
	if (h) {
		Goto(h);
		OnHandleChanged();
	}
	HZQDB user = GetUser();
	if (user) {
		GotoUser(user);
		OnUserChanged();
	}
}

void MyTechFrame::OnHandleChanged()
{
	Freeze();
	//
	auto sizer = GetSizer();
	auto h = Get();
	ASSERT(h && h->type == ZQDB_HANDLE_TYPE_CODE);
	zqdb::Code code(h);
	zqdb::Product product(code.GetProduct());
	wxCSConv utf8cv(wxFONTENCODING_UTF8);
	auto frame = wxGetApp().GetFrame();
	if (frame && frame != this) {
		wxString title = wxString::Format(wxT("%s %s")
			, utf8cv.cMB2WX(code->Name).data()
			, utf8cv.cMB2WX(code->Code).data()
		);
		SetTitle(title);
	}
	if (code_view_) {
		code_view_->SetHandle(h);
	}
	if (tech_view_) {
		//切换代码时，先移除所有划线，后期需要做提示是否保存划线
		auto major = tech_view_->GetMajorView();
		if (major) {
			major->RemoveAllDrawline();
		}
		tech_view_->SetHandle(h);
	}
	if (title_view_) {
		title_view_->SetHandle(h);
	}
	if (mmp_view_) {
		sizer->Show(mmp_view_, product->Type != PRODUCT_TYPE_Index, true);
		mmp_view_->SetHandle(h);
	}
	if (trade_view_) {
		trade_view_->SetHandle(h);
	}
	if (info_view_) {
		info_view_->SetHandle(h);
	}
	if (tick_view_) {
		tick_view_->SetHandle(h);
	}
	if (user_view_) {
		user_view_->SetHandle(h);
	}

	ShowF10(true);

	//前进后退时已经设置过了record_pos_，这里不用再记录了
	if (record_list_.empty() || record_list_[record_pos_] != h) {
		size_t i = 0, j = record_list_.size();
		for (; i < j; ++i)
		{
			if (record_list_[i] == h) {
				auto record = record_list_[i];
				size_t k = i;
				for (; (k + 1) < j; ++k) {
					record_list_[k] = record_list_[k + 1];
				}
				record_pos_ = k;
				record_list_[k] = record;
				break;
			}
		}
		if (i >= j) {
			record_pos_ = record_list_.size();
			record_list_.emplace_back(h);
		}
	}
	//
	Refresh();
	Layout();
	Thaw();
}

void MyTechFrame::OnUserChanged()
{
	Freeze();
	//
	if (tech_view_) {
		tech_view_->SetUser(GetUser());
	}
	if (user_view_) {
		user_view_->SetUser(GetUser());
	}
	if (trade_view_) {
		trade_view_->SetUser(GetUser());
	}
	//
	Refresh();
	Layout();
	Thaw();
}

void MyTechFrame::Set(HZQDB h)
{
	auto h_old = Get();
	if (h == h_old) {
		return;
	}
	if (h_old) {
		UnSubscribe(h_old);
	}
	ContainerMap::Set(h);
	Subscribe(h);
}

void MyTechFrame::SetUser(HZQDB user)
{
	auto old_user = GetUser();
	if (user == old_user) {
		return;
	}
	if (old_user) {
		ZQDBRefresh(old_user, -1, 0);
	}
	ContainerMap::SetUser(user);
	ZQDBRefresh(user, 3 * 1000, 0);
}

void MyTechFrame::Goto(HZQDB h)
{
	switch (h->type)
	{
	case ZQDB_HANDLE_TYPE_EXCHANGE: {
		zqdb::Exchange exchange(h);
		ShowContainer(utf2wxString(exchange->Name), zqdb::Calc::StdContainer(h));
	} break;
	case ZQDB_HANDLE_TYPE_PRODUCT: {
		zqdb::Product product(h);
		ShowContainer(utf2wxString(product->Name), zqdb::Calc::StdContainer(h));
	} break;
	case ZQDB_HANDLE_TYPE_CODE: {
		if (!code_view_->Goto(h)) {
#if 0
			auto flags = ZQDBGetFlags(h);
			if (flags & ZQDB_CODE_FLAG_SELFSEL) {
				code_view_->ShowKey(wxT(SMARTKB_KEY_SELFSEL), h);
			}
			else if (ZQDBIsSubscribeMarketData(h)) {
				code_view_->ShowKey(wxT(SMARTKB_KEY_SUBSCRIBE), h);
			}
			else {
				auto hproduct = ZQDBGetParent(h);
				zqdb::Product product(hproduct);
				code_view_->ShowContainer(utf2wxString(product->Name), zqdb::Calc::StdContainer(hproduct), h);
			}
#else
			code_view_->ShowKey(wxT(SMARTKB_KEY_ALL), h);
			InnerUpdateNavigateKey(wxT(SMARTKB_KEY_ALL));
#endif
		}
	} break;
	case ZQDB_HANDLE_TYPE_CATEGORY: {
		ShowCategory(h);
	} break;
	}
}

void MyTechFrame::AddSelfSel(HZQDB h)
{
	wxGetApp().AddSelfSel(h);
}

void MyTechFrame::RemoveSelfSel(HZQDB h)
{
	wxGetApp().RemoveSelfSel(h);
}

void MyTechFrame::Subscribe(HZQDB h)
{
	wxGetApp().Subscribe(h);
}

void MyTechFrame::UnSubscribe(HZQDB h)
{
	wxGetApp().UnSubscribe(h);
}

void MyTechFrame::Goto(size_t pos)
{
	if (pos < record_list_.size()) {
		record_pos_ = pos;
		auto record = record_list_[record_pos_];
		code_view_->Goto(record);
	}
}

void MyTechFrame::Backward()
{
	if (record_pos_ > 0 && !record_list_.empty()) {
		--record_pos_;
		auto record = record_list_[record_pos_];
		code_view_->Goto(record);
	}
}

void MyTechFrame::Forward()
{
	if ((record_pos_ + 1) < record_list_.size()) {
		++record_pos_;
		auto record = record_list_[record_pos_];
		code_view_->Goto(record);
	}
}

void MyTechFrame::ShowKey(const wxString& key)
{
	code_view_->ShowKey(key);
	InnerUpdateNavigateKey(key);
}

void MyTechFrame::ShowContainer(const wxString& key, const zqdb::Calc::Container& container)
{
	code_view_->ShowContainer(key, container);
	InnerUpdateNavigateKey(key);
}

void MyTechFrame::GotoUser(HZQDB user)
{
	user_view_->Goto(user);
}

#ifdef _SORT

int MyTechFrame::IsSort(MY_CODE_SORT_TYPE* type, size_t* secs)
{
	return code_view_->IsSort(type, secs);
}

void MyTechFrame::SortByZD(MY_CODE_SORT_TYPE type, size_t duration, int sort)
{
	return code_view_->SortByZD(type, duration, sort);
}

void MyTechFrame::SortByField(MDB_FIELD& field, int sort)
{
	return code_view_->SortByField(field, sort);
}

void MyTechFrame::SortByCalc(const zqdb::Calc::Sort& calc, int sort)
{
	return code_view_->SortByCalc(calc, sort);
}

#endif

void MyTechFrame::OnNotifyStatus(HZQDB h)
{
	if (h->type == ZQDB_HANDLE_TYPE_MODULE) {
		if (ZQDBIsDisable(h))
			;
		else {
			InnerUpdateNavigate();
			m_ribbon->Realize();
		}
	}
	if (code_view_) {
		code_view_->OnNotifyStatus(h);
	}
	if (tech_view_) {
		tech_view_->OnNotifyStatus(h);
	}
	if (title_view_) {
		title_view_->OnNotifyStatus(h);
	}
	if (mmp_view_) {
		mmp_view_->OnNotifyStatus(h);
	}
	if (trade_view_) {
		trade_view_->OnNotifyStatus(h);
	}
	if (info_view_) {
		info_view_->OnNotifyStatus(h);
	}
	if (tick_view_) {
		tick_view_->OnNotifyStatus(h);
	}
	if (user_view_) {
		user_view_->OnNotifyStatus(h);
	}
	if (status_bar_) {
		status_bar_->OnNotifyStatus(h);
	}
}

void MyTechFrame::OnNotifyAdd(HZQDB h)
{
	if (h->type == ZQDB_HANDLE_TYPE_CATEGORY) {
		all_category_.Update();
		InnerUpdateNavigate();
		m_ribbon->Realize();
	}
	if (code_view_) {
		code_view_->OnNotifyAdd(h);
	}
	if (tech_view_) {
		tech_view_->OnNotifyAdd(h);
	}
	if (title_view_) {
		title_view_->OnNotifyAdd(h);
	}
	if (mmp_view_) {
		mmp_view_->OnNotifyAdd(h);
	}
	if (trade_view_) {
		trade_view_->OnNotifyAdd(h);
	}
	if (info_view_) {
		info_view_->OnNotifyAdd(h);
	}
	if (tick_view_) {
		tick_view_->OnNotifyAdd(h);
	}
	if (user_view_) {
		user_view_->OnNotifyAdd(h);
	}
	if (status_bar_) {
		status_bar_->OnNotifyAdd(h);
	}
}

void MyTechFrame::OnNotifyRemove(HZQDB h)
{
	if (h->type == ZQDB_HANDLE_TYPE_CATEGORY) {
		all_category_.Update();
		InnerUpdateNavigate();
		m_ribbon->Realize();
	}
	if (code_view_) {
		code_view_->OnNotifyRemove(h);
	}
	if (tech_view_) {
		tech_view_->OnNotifyRemove(h);
	}
	if (title_view_) {
		title_view_->OnNotifyRemove(h);
	}
	if (mmp_view_) {
		mmp_view_->OnNotifyRemove(h);
	}
	if (trade_view_) {
		trade_view_->OnNotifyRemove(h);
	}
	if (info_view_) {
		info_view_->OnNotifyRemove(h);
	}
	if (tick_view_) {
		tick_view_->OnNotifyRemove(h);
	}
	if (user_view_) {
		user_view_->OnNotifyRemove(h);
	}
	if (status_bar_) {
		status_bar_->OnNotifyRemove(h);
	}
}

void MyTechFrame::OnNotifyUpdate(HZQDB h)
{
	bool relayout = false;
	switch (h->type)
	{
	case ZQDB_HANDLE_TYPE_MODULE: {
	} break;
	case ZQDB_HANDLE_TYPE_CALCFUNC: {
		zqdb::Calc::Func func(h);
		switch (func.GetCalcType())
		{
		case CALC_MAJOR: {

		} break;
		case CALC_MINOR: {

		} break;
		case CALC_CONTAINER: {
			all_container_func_.Update();
			InnerUpdateNavigate();
			m_ribbon->Realize();
		} break;
		case CALC_FILTER: {
			all_filter_func_.Update();
			InnerUpdateNavigate();
			m_ribbon->Realize();
		} break;
		case CALC_SORT: {
#ifdef _SORT
			all_sort_func_.Update();
			InnerUpdateSort();
			m_ribbon->Realize();
#endif
		} break;
		case CALC_SCRIPT: {
			all_script_func_.Update();
			InnerUpdateScript();
			m_ribbon->Realize();

		} break;
		case CALC_STRATEGY: {

		} break;
		}
	} break;
	default: {
	} break;
	}

	zqdb::AllExchange allexchange;
	if (code_view_) {
		code_view_->OnNotifyUpdate(h);
	}
	if (tech_view_) {
		tech_view_->OnNotifyUpdate(h);
	}
	if (title_view_) {
		title_view_->OnNotifyUpdate(h);
	}
	if (mmp_view_) {
		auto level = mmp_view_->GetLevel();
		mmp_view_->OnNotifyUpdate(h);
		if (level != mmp_view_->GetLevel()) {
			relayout = true;
		}
	}
	if (trade_view_) {
		trade_view_->OnNotifyUpdate(h);
	}
	if (info_view_) {
		info_view_->OnNotifyUpdate(h);
	}
	if (tick_view_) {
		tick_view_->OnNotifyUpdate(h);
	}
	if (user_view_) {
		user_view_->OnNotifyUpdate(h);
	}
	if (status_bar_) {
		status_bar_->OnNotifyUpdate(h);
	}
	if (relayout) {
		Refresh();
		Layout();
	}
}

void MyTechFrame::ShowExpand()
{
	wxSizer* sizerTop = GetSizer();
	auto sizerMain = sizerTop->GetItem(2)->GetSizer();
	auto sizerListItem = sizerMain->GetItem((size_t)0);
	auto szList = sizerListItem->GetSize();
	if (expand_) {
		expand_ = false;
		//sizerListItem->SetProportion(0);
		sizerListItem->SetInitSize(szList.x / 2, szList.y);
	}
	else {
		expand_ = true;
		//sizerListItem->SetProportion(1);
		sizerListItem->SetInitSize(szList.x * 2, szList.y);
	}
	Layout();
}

void MyTechFrame::ShowTips()
{
	//SetStatusText(wxGetApp().GetLastTips());
	if (status_bar_) {
		status_bar_->ShowTips();
	}
}

void MyTechFrame::OnNavigateHome(wxRibbonButtonBarEvent& WXUNUSED(evt))
{
}

void MyTechFrame::OnNavigateBackward(wxRibbonButtonBarEvent& WXUNUSED(evt))
{
	Backward();
}

void MyTechFrame::OnNavigateBackwardDropdown(wxRibbonButtonBarEvent& evt)
{
	//wxCommandEvent;
	//auto infos = m_history.BackwardInfo();
	//GenerateDropdownEventMenuImpl(evt, infos, false);
	if (record_pos_ > 0) {
		wxMenu menu;
		for(size_t i = 0; i < record_pos_; i++)
		{
			zqdb::Code code(record_list_[i]);
			menu.Append(ID_NAVIGATE_GOTO + i, utf2wxString(code->Name));
		}
		evt.PopupMenu(&menu);
	}
}

void MyTechFrame::OnNavigateBackwardUpdateUI(wxUpdateUIEvent& evt)
{
	evt.Enable(record_pos_ > 0);
}

void MyTechFrame::OnNavigateForward(wxRibbonButtonBarEvent& WXUNUSED(evt))
{
	Forward();
}

void MyTechFrame::OnNavigateForwardDropdown(wxRibbonButtonBarEvent& evt)
{
	if ((record_pos_ + 1) < record_list_.size()) {
		wxMenu menu;
		for (size_t i = record_pos_ + 1; i < record_list_.size(); i++)
		{
			zqdb::Code code(record_list_[i]);
			menu.Append(ID_NAVIGATE_GOTO + i, utf2wxString(code->Name));
		}
		evt.PopupMenu(&menu);
	}
}

void MyTechFrame::OnNavigateForwardUpdateUI(wxUpdateUIEvent& evt)
{
	evt.Enable((record_pos_ + 1) < record_list_.size());
}

void MyTechFrame::OnNavigateGoto(wxCommandEvent& evt)
{
	auto id = evt.GetId();
	if (id >= ID_NAVIGATE_GOTO && id < ID_NAVIGATE_GOTO_MAX)
	{
		auto pos = id - ID_NAVIGATE_GOTO;
		Goto(pos);
	}
}

void MyTechFrame::OnNavigateUp(wxRibbonButtonBarEvent& WXUNUSED(evt))
{
	//Up();
}

void MyTechFrame::OnNavigateDown(wxRibbonButtonBarEvent& WXUNUSED(evt))
{
	//Down();
}

void MyTechFrame::OnNavigateFind(wxRibbonButtonBarEvent& evt)
{
	wxGetApp().GetSmartKBDlg()->ShowFor(this);
}

void MyTechFrame::OnNavigateFindDropdown(wxRibbonButtonBarEvent& evt)
{
	wxRibbonButtonBar* pBar = evt.GetBar();
	if (pBar) {
		wxMenu menu;
		wxGetApp().ClearMenuMap();
		const auto& systemsel = wxGetApp().systemsel();
		if (!systemsel.empty()) {
			for (size_t i = 0; i < systemsel.size(); ++i)
			{
				auto name = utf2wxString(systemsel[i].c_str());
				if (name.empty()) {
					menu.AppendSeparator();
				}
				else {
					menu.Append(ID_NAVIGATE_FIND_SYSTEM + i, name);
				}
			}
			menu.AppendSeparator();
		}
		menu.Append(ID_NAVIGATE_FIND_SIMILARITY_CLOUD, wxT("相似选股"));
		//menu.AppendSeparator();
		//menu.Append(ID_NAVIGATE_FIND_SIMILARITY_SETTINGS, wxT("相似选股设置"));
		evt.PopupMenu(&menu);
	}
}

void MyTechFrame::OnNavigateFindSystemGoto(wxCommandEvent& evt)
{
	auto i = evt.GetId() - ID_NAVIGATE_FIND_SYSTEM;
	ShowSystemSel(i);
}

void MyTechFrame::OnNavigateFindSimilarity(wxRibbonButtonBarEvent& WXUNUSED(evt))
{
	ShowSimilarity(wxT("相似选股"), 200, -1);
}

void MyTechFrame::OnNavigateFindSimilarityGoto(wxCommandEvent& evt)
{
	switch (evt.GetId())
	{
	case ID_NAVIGATE_FIND_SIMILARITY: {
		ShowSimilarity(wxT("相似选股"), 200, -1);
	} break;
	case ID_NAVIGATE_FIND_SIMILARITY_CLOUD: {
		ShowSimilarity(wxT("云相似选股"), 200, -1, nullptr, SHOW_SIMILARITY_FLAG_CLOUD);
	} break;
	}
}

void MyTechFrame::OnNavigateAll(wxRibbonButtonBarEvent& evt)
{
	ShowKey(wxT(SMARTKB_KEY_ALL));
}

void MyTechFrame::OnNavigateAllDropdown(wxRibbonButtonBarEvent& evt)
{
	wxRibbonButtonBar* pBar = evt.GetBar();
	if (pBar) {
		wxMenu menu;
		wxGetApp().ClearMenuMap();
		size_t exchange_id = ID_NAVIGATE_EXCHANGE;
		size_t product_id = ID_NAVIGATE_EXCHANGE_PRODUCT;
		zqdb::AllExchange allexchange;
		for (size_t i = 0; i < allexchange.size(); i++)
		{
			wxMenu *submenu = new wxMenu;
			zqdb::Exchange exchange(allexchange[i]);
			auto menu_item = submenu->Append(exchange_id, utf2wxString(exchange->Name));
			wxGetApp().SetMenuData(exchange_id++, allexchange[i]);
			submenu->AppendSeparator();
			zqdb::AllProduct allproduct(allexchange[i]);
			/*std::sort(allproduct.begin(), allproduct.end(), [](HZQDB x, HZQDB y) {
			zqdb::Product xproduct(x);
			zqdb::Product yproduct(y);
			return strcmp(xproduct->Name, yproduct->Name) < 0;
			});*/
			for (size_t j = 0; j < allproduct.size(); j++)
			{
				zqdb::Product product(allproduct[j]);
				submenu->Append(product_id, utf2wxString(product->Name));
				wxGetApp().SetMenuData(product_id++, allproduct[j]);
			}
			menu.AppendSubMenu(submenu, utf2wxString(exchange->Name));
		}
		evt.PopupMenu(&menu);
	}
}

void MyTechFrame::OnNavigateAllGoto(wxCommandEvent& evt)
{
	auto id = evt.GetId();
	if ((id >= ID_NAVIGATE_EXCHANGE && id < ID_NAVIGATE_EXCHANGE_MAX)
		|| (id >= ID_NAVIGATE_EXCHANGE_PRODUCT && id < ID_NAVIGATE_EXCHANGE_PRODUCT_MAX)) {
		HZQDB h = (HZQDB)wxGetApp().GetMenuData(id);
		if (h) {
			Goto(h);
		}
	}
}

void MyTechFrame::OnMarketAll(wxRibbonButtonBarEvent& evt)
{
	ShowKey(wxT(SMARTKB_KEY_ALL));
}

void MyTechFrame::OnMarketAllDropdown(wxRibbonButtonBarEvent& evt)
{
	wxRibbonButtonBar* pBar = evt.GetBar();
	if (pBar) {
		wxMenu menu;
		wxGetApp().ClearMenuMap();
		if (!all_category_.empty()) {
			size_t cat_id = ID_MARKET_CATEGORY_MENU;
			const char* cats[] = { u8"股票"
				, u8"债券"
				, u8"基金"
				, u8"期货"
			};
			for (auto str : cats)
			{
				HZQDB h = nullptr;
				for (size_t i = 0, j = all_category_.size(); i < j; ++i)
				{
					auto name = ZQDBGetCategoryName(all_category_[i]);
					if (strcmp(name, str) == 0) {
						h = all_category_[i];
						break;
					}
				}
				if (!h) {
					continue;
				}
#if 0
				zqdb::Category topcat(h);
				if (topcat) {
					auto menu_item = menu.Append(ID_MARKET_CATEGORY + i, utf2wxString(topcat.Name()));
					wxGetApp().SetMenuData(ID_MARKET_CATEGORY + i, (HZQDB)topcat);
				}
#else
				zqdb::AllCategory allcat(h);
				if (!allcat.empty()) {
					for (size_t i = 0, j = allcat.size(), k = 0; i <j; i++)
					{
						if (allcat[i]) {
							zqdb::Category cat(allcat[i]);
							auto menu_item = menu.Append(cat_id, utf2wxString(cat.Name()));
							wxGetApp().SetMenuData(cat_id, allcat[i]);
							cat_id++;
						}
					}
					menu.AppendSeparator();
				}
#endif
			}
		}
		size_t exchange_id = ID_MARKET_EXCHANGE;
		size_t product_id = ID_MARKET_EXCHANGE_PRODUCT;
		zqdb::AllExchange allexchange;
		for (size_t i = 0, j = ZQDBGetModuleCount(); i < j; i++)
		{
			auto hmodule = ZQDBGetModuleAt(i);
			for (auto hexchange : allexchange)
			{
				if (ZQDBGetModule(hexchange) != hmodule) {
					continue;
				}
				wxMenu *submenu = new wxMenu;
				zqdb::Exchange exchange(hexchange);
				auto menu_item = submenu->Append(exchange_id, utf2wxString(exchange->Name));
				wxGetApp().SetMenuData(exchange_id++, hexchange);
				submenu->AppendSeparator();
				zqdb::AllProduct allproduct(hexchange);
				/*std::sort(allproduct.begin(), allproduct.end(), [](HZQDB x, HZQDB y) {
				zqdb::Product xproduct(x);
				zqdb::Product yproduct(y);
				return strcmp(xproduct->Name, yproduct->Name) < 0;
				});*/
				for (auto hproduct : allproduct)
				{
					zqdb::Product product(hproduct);
					submenu->Append(product_id, utf2wxString(product->Name));
					wxGetApp().SetMenuData(product_id++, hproduct);
				}
				menu.AppendSubMenu(submenu, utf2wxString(exchange->Name));
			}
		}
		evt.PopupMenu(&menu);
	}
}

void MyTechFrame::OnMarketAllGoto(wxCommandEvent& evt)
{
	auto id = evt.GetId();
	if ((id >= ID_MARKET_CATEGORY_MENU && id < ID_MARKET_CATEGORY_MENU_MAX)
		|| (id >= ID_MARKET_EXCHANGE && id < ID_MARKET_EXCHANGE_MAX)
		|| (id >= ID_MARKET_EXCHANGE_PRODUCT && id < ID_MARKET_EXCHANGE_PRODUCT_MAX)) {
		HZQDB h = (HZQDB)wxGetApp().GetMenuData(id);
		if (h) {
			switch (h->type)
			{
			case ZQDB_HANDLE_TYPE_EXCHANGE: {
				zqdb::Exchange exchange(h);
				ShowContainer(utf2wxString(exchange->Name), zqdb::Calc::StdContainer(h));
			} break;
			case ZQDB_HANDLE_TYPE_PRODUCT: {
				zqdb::Product product(h);
				ShowContainer(utf2wxString(product->Name), zqdb::Calc::StdContainer(h));
			} break;
			case ZQDB_HANDLE_TYPE_CATEGORY: {
				zqdb::Category cat(h);
				ShowContainer(utf2wxString(cat.Name()), zqdb::Calc::StdContainer(h));
			} break;
			}
		}
	}
}

void MyTechFrame::OnMarketExchange(wxRibbonButtonBarEvent& evt)
{
	wxRibbonButtonBar* pBar = evt.GetBar();
	if (pBar) {
		HZQDB h = static_cast<HZQDB>(pBar->GetItemClientData(evt.GetButton()));
		zqdb::Exchange exchange(h);
		ShowContainer(utf2wxString(exchange->Name), zqdb::Calc::StdContainer(h));
	}
}

void MyTechFrame::OnExchangeDropdown(wxRibbonButtonBarEvent& evt)
{
	wxRibbonButtonBar* pBar = evt.GetBar();
	if (pBar) {
		HZQDB h = static_cast<HZQDB>(pBar->GetItemClientData(evt.GetButton()));
		zqdb::AllProduct allproduct(h);
		/*std::sort(allproduct.begin(), allproduct.end(), [](HZQDB x, HZQDB y) {
			zqdb::Product xproduct(x);
			zqdb::Product yproduct(y);
			return strcmp(xproduct->Name, yproduct->Name) < 0;
		});*/
		wxMenu menu;
		wxGetApp().ClearMenuMap();
		for (size_t k = 0; k < allproduct.size(); k++)
		{
			zqdb::Product product(allproduct[k]);
			auto menu_item = menu.Append(ID_MARKET_PRODUCT + k, utf2wxString(product->Name));
			wxGetApp().SetMenuData(ID_MARKET_PRODUCT + k, allproduct[k]);
		}
		evt.PopupMenu(&menu);
	}
}

void MyTechFrame::OnProductGoto(wxCommandEvent& evt)
{
	auto id = evt.GetId();
	if (id >= ID_MARKET_PRODUCT && id < ID_MARKET_PRODUCT_MAX)
	{
		HZQDB h = (HZQDB)wxGetApp().GetMenuData(id);
		if (h) {
			zqdb::Product product(h);
			ShowContainer(utf2wxString(product->Name), zqdb::Calc::StdContainer(h));
		}
	}
}

void MyTechFrame::OnMarketProduct(wxRibbonButtonBarEvent& evt)
{
	wxRibbonButtonBar* pBar = evt.GetBar();
	if (pBar) {
		HZQDB h = static_cast<HZQDB>(pBar->GetItemClientData(evt.GetButton()));
		Goto(h);
	}
}

void MyTechFrame::OnMarketSelf(wxRibbonButtonBarEvent& evt)
{
	ShowKey(wxT(SMARTKB_KEY_SELFSEL));
}

void MyTechFrame::OnMarketSubscribe(wxRibbonButtonBarEvent& evt)
{
	ShowKey(wxT(SMARTKB_KEY_SUBSCRIBE));
}

void MyTechFrame::OnMarketSubscribeDropdown(wxRibbonButtonBarEvent& evt)
{
	/*wxRibbonButtonBar* pBar = evt.GetBar();
	if (pBar) {
		wxMenu menu;
		wxGetApp().ClearMenuMap();
		menu.Append(ID_MARKET_SELF, wxT(SMARTKB_KEY_SUBSCRIBE));
		evt.PopupMenu(&menu);
	}*/
	OnMarketAllDropdown(evt);
}

void MyTechFrame::OnMarketSubscribeGoto(wxCommandEvent& evt)
{
	//ShowKey(wxT(SMARTKB_KEY_SUBSCRIBE));
}
//
//void MyTechFrame::OnMarketSelfSel(wxRibbonButtonBarEvent& evt)
//{
//	ShowKey(wxT(SMARTKB_KEY_SELFSEL));
//}
//
//void MyTechFrame::OnMarketSelfSelDropdown(wxRibbonButtonBarEvent& evt)
//{
//}

void MyTechFrame::ShowCategory(HZQDB h)
{
	if (h && h->type == ZQDB_HANDLE_TYPE_CATEGORY) {
		zqdb::Category cat(h);
		wxString strKey = utf2wxString(cat.Name());
		zqdb::Category cat_parent(cat.Parent());
		while (cat_parent) {
			strKey += wxT(".");
			strKey += utf2wxString(cat_parent.Name());
			cat_parent = zqdb::Category(cat_parent.Parent());
		}
		switch (cat.Type())
		{
		case ZQDB_HANDLE_TYPE_EXCHANGE:
		case ZQDB_HANDLE_TYPE_PRODUCT:
		case ZQDB_HANDLE_TYPE_CODE: {
			ShowContainer(strKey, zqdb::Calc::StdContainer(h));
		} break;
		case ZQDB_HANDLE_TYPE_CATEGORY: {
			zqdb::AllCategory allcat(h);
			if (allcat.empty()) {
				break;
			}
			zqdb::Category sub_cat(allcat[0]);
			ShowContainer(utf2wxString(sub_cat.Name()) + wxT(".") + strKey, zqdb::Calc::StdContainer(sub_cat));
		} break;
		}
		InnerUpdateNavigateKey(strKey);
	}
}

void MyTechFrame::OnMarketCategory(wxRibbonButtonBarEvent& evt)
{
	wxRibbonButtonBar* pBar = evt.GetBar();
	if (pBar) {
		HZQDB h = static_cast<HZQDB>(pBar->GetItemClientData(evt.GetButton()));
		ShowCategory(h);
	}
}

void MyTechFrame::OnMarketCategoryDropdown(wxRibbonButtonBarEvent& evt)
{
	wxRibbonButtonBar* pBar = evt.GetBar();
	if (pBar) {
		HZQDB h = static_cast<HZQDB>(pBar->GetItemClientData(evt.GetButton()));
		if (h && h->type == ZQDB_HANDLE_TYPE_CATEGORY) {
			zqdb::AllCategory allcat(h);
			if (!allcat.empty()) {
				wxMenu menu;
				wxGetApp().ClearMenuMap();
				for (size_t i = 0, j = allcat.size(), k = 0; i <j; i++)
				{
					if ((i - k) > 25) {
						k = i;
						menu.Break();
					}
					if (allcat[i]) {
						zqdb::Category cat(allcat[i]);
						auto menu_item = menu.Append(ID_MARKET_CATEGORY_MENU + i, utf2wxString(cat.Name()));
						wxGetApp().SetMenuData(ID_MARKET_CATEGORY_MENU + i, allcat[i]);
					}
					else if(i < (j - 1)) {
						menu.AppendSeparator();
					}
				}
				evt.PopupMenu(&menu);
			}
		}
	}
}

void MyTechFrame::OnMarketCategoryGoto(wxCommandEvent& evt)
{
	auto h = (HZQDB)wxGetApp().GetMenuData(evt.GetId());
	ShowCategory(h);
}

void MyTechFrame::OnContainer(wxRibbonButtonBarEvent& evt)
{
	size_t pos = evt.GetId() - ID_CONTAINER;
	if (pos < all_container_func_.size()) {
		wxRibbonButtonBar* pBar = evt.GetBar();
		if (pBar) {
			zqdb::Calc::Func func(all_container_func_[pos]);
			char buf[260] = { 0 };
			auto name = func.GetAttrAsStr("name", buf, 260);
			wxString label = utf2wxString(name[0] ? name : func.GetCalcName());
			zqdb::Calc::Container container(func.GetCalcName(), nullptr);
			ShowContainer(label, container);
		}
	}
}

void MyTechFrame::OnContainerDropdown(wxRibbonButtonBarEvent& evt)
{
	size_t pos = evt.GetId() - ID_CONTAINER;
	if (pos >= all_container_func_.size()) {
		return;
	}
	wxRibbonButtonBar* pBar = evt.GetBar();
	if (!pBar) {
		return;
	}
	zqdb::Calc::Func func(all_container_func_[pos]);
	if (func.HasAttr("menu")) {
		char buf[4096] = { 0 };
		boost::property_tree::ptree cfg;
		XUtil::json_from_str(func.GetAttrAsStrEx("", buf, 4096), cfg);
		auto opt_menu = cfg.get_child_optional("menu");
		if (!opt_menu) {
			return;
		}
		auto& cfg_menu = opt_menu.get();
		if (cfg_menu.empty()) {
			return;
		}
		wxMenu menu;
		wxGetApp().ClearMenuMap();
		size_t id = ID_CONTAINER_MENU;
		for (auto it = cfg_menu.begin(); it != cfg_menu.end(); ++id, ++it)
		{
			auto name = it->second.get<std::string>("name");
			if (name.empty()) {
				menu.AppendSeparator();
			}
			else {
				menu.Append(id, utf2wxString(name.c_str()));
				wxGetApp().SetMenuData(id, &cfg);
			}
		}
		menu.AppendSeparator();
		auto menu_item = menu.Append(ID_CALC_EDIT_CONTAINER + pos, wxT("修改代码…"));
		zqdb::SetMenuItemClientData(menu_item, func);
		wxGetApp().SetMenuData(ID_CONTAINER_MENU_MAX, all_container_func_[pos]);
		evt.PopupMenu(&menu);
	}
	else {
		wxMenu menu;
		wxGetApp().ClearMenuMap();
		bool test = false;
		for (size_t i = 0, j = all_container_func_.size(); i < j; i++)
		{
			zqdb::Calc::Func func(all_container_func_[i]);
			if (func.HasAttr("menu")) {
				continue;
			}
			if (i == cur_container_func_) {
				continue;
			}
			else {
				char buf[260] = { 0 };
				auto type = func.GetAttrAsStr("type", buf, 260);
				if (strcmp(type, "test") == 0) {
					test = true;
					continue;
				}
				auto name = func.GetAttrAsStr("name", buf, 260);
				wxString label = utf2wxString(name[0] ? name : func.GetCalcName());
				auto menu_item = menu.Append(ID_CONTAINER + i, label);
			}
			wxGetApp().SetMenuData(ID_CONTAINER + i, all_container_func_[i]);
		}
		if (test) {
			menu.AppendSeparator();
			for (size_t i = 0, j = all_container_func_.size(); i < j; i++)
			{
				zqdb::Calc::Func func(all_container_func_[i]);
				if (func.HasAttr("menu")) {
					continue;
				}
				if (i == cur_container_func_) {
					continue;
				}
				else {
					char buf[260] = { 0 };
					auto type = func.GetAttrAsStr("type", buf, 260);
					if (strcmp(type, "test") != 0) {
						continue;
					}
					auto name = func.GetAttrAsStr("name", buf, 260);
					wxString label = utf2wxString(name[0] ? name : func.GetCalcName());
					auto menu_item = menu.Append(ID_CONTAINER + i, label);
				}
				wxGetApp().SetMenuData(ID_CONTAINER + i, all_container_func_[i]);
			}
		}
		evt.PopupMenu(&menu);
	}
}

void MyTechFrame::OnContainerGoto(wxCommandEvent& evt)
{
	if (evt.GetId() >= ID_CONTAINER_MENU && evt.GetId() < ID_CONTAINER_MENU_MAX) {
		auto h = (HZQDB)wxGetApp().GetMenuData(ID_CONTAINER_MENU_MAX);
		if (!h) {
			return;
		}
		auto p_cfg = (boost::property_tree::ptree*)wxGetApp().GetMenuData(evt.GetId());
		if (p_cfg) {
			auto& cfg = *p_cfg;
			auto opt_menu = cfg.get_child_optional("menu");
			if (!opt_menu) {
				return;
			}
			auto& cfg_menu = opt_menu.get();
			if (cfg_menu.empty()) {
				return;
			}
			size_t id = ID_CONTAINER_MENU;
			for (auto it = cfg_menu.begin(); it != cfg_menu.end(); ++id, ++it)
			{
				if (evt.GetId() == id) {
					zqdb::Calc::Func func(h);
					auto name = it->second.get<std::string>("name");
					wxString label = utf2wxString(name.c_str());
					std::string str;
					XUtil::json_to_str(str, it->second.get_child("input"));
					zqdb::Calc::InputAttr input(str.c_str(), XUtil::XML_FLAG_JSON_STRING);
					zqdb::Calc::Container container(func.GetCalcName(), input);
					ShowContainer(label, container);
					break;
				}
			}
		}
	}
	else {
		size_t pos = evt.GetId() - ID_CONTAINER;
		if (pos < all_container_func_.size()) {
			InnerSetCurContainer(pos);
			m_ribbon->Realize();
			zqdb::Calc::Func func(all_container_func_[pos]);
			char buf[260] = { 0 };
			auto name = func.GetAttrAsStr("name", buf, 260);
			wxString label = utf2wxString(name[0] ? name : func.GetCalcName());
			zqdb::Calc::Container container(func.GetCalcName(), nullptr);
			ShowContainer(label, container);
		}
	}
}

void MyTechFrame::OnNavigateSetting(wxRibbonPanelEvent& evt)
{
	zqdb::TechDlg dlg(this);
	dlg.ShowModal();
}

void MyTechFrame::ShowSystemSel(size_t index)
{
	const auto& systemsel = wxGetApp().systemsel();
	if (index >= systemsel.size()) {
		return;
	}
	auto key = utf2wxString(systemsel[index].c_str());
	bool market_value_flag[2] = { false, false };
	double market_value[2] = { 0, 0 };
	bool PE_flag[2] = { false, false };
	double PE[2] = { 0, 0 };
	if (ZQDBIsClient()) {
		if (ZQDBGetUserPoints() < 200) {
			if (wxOK == wxMessageBox(wxString::Format(wxT("您不能使用%s选股\n请联系我们获取高级授权码"), key), wxT("提示"), wxOK | wxCANCEL)) {
				wxLaunchDefaultBrowser(wxT(PROJECT_CONTACT_URL));
			}
			return;
		}
		MySelectDlg dlg(this);
		if (wxID_OK != dlg.ShowModal()) {
			return;
		}
		market_value_flag[0] = dlg.GetMarketValueMin(market_value[0]);
		market_value_flag[1] = dlg.GetMarketValueMax(market_value[1]);
		PE_flag[0] = dlg.GetPEMin(PE[0]);
		PE_flag[1] = dlg.GetPEMax(PE[1]);
	}
	else {
		return;
	}
	zqdb::Calc::StdContainer container;
	zqdb::AllCode cur; //默认筛选全部
	auto count = cur.size();
	wxProgressDialog dlg(wxT("筛选"),
		// "Reserve" enough space for the multiline
		// messages below, we'll change it anyhow
		// immediately in the loop below
		wxString(' ', 100) + "\n",
		count,    // range
		this,   // parent
		wxPD_CAN_ABORT |
		wxPD_CAN_SKIP |
		wxPD_APP_MODAL |
		//wxPD_AUTO_HIDE | // -- try this as well
		//wxPD_ELAPSED_TIME |
		//wxPD_ESTIMATED_TIME |
		//wxPD_REMAINING_TIME |
		wxPD_SMOOTH // - makes indeterminate mode bar on WinXP very small
	);
	bool cont = true;
	if (1) {
		//云选股
		com::zqdb::proto::msg::ReqQrySystemSel req_data;
		req_data.set_name(systemsel[index]);
		if (1) {
			for (size_t i = count; i > 0; i--)
			{
				zqdb::Code ycode(cur[i - 1]);
				zqdb::Product yproduct(ycode.GetProduct());
				if (yproduct->Type != PRODUCT_TYPE_Stock) {
					cur.erase(cur.begin() + (i - 1));
				}
				else {
					zqdb::CodeT<tagStockCodeInfo> ystock(ycode);
					if ((market_value_flag[0] && ystock->MarketValue < market_value[0])
						|| (market_value_flag[1] && ystock->MarketValue > market_value[1])
						|| (PE_flag[0] && ystock->PE_TTM < PE[0])
						|| (PE_flag[1] && ystock->PE_TTM > PE[1])) {
						cur.erase(cur.begin() + (i - 1));
					}
				}
			}
			size_t real_count = cur.size();
			for (size_t i = 0; i < real_count; i += 100)
			{
				bool skip = false;
				cont = dlg.Update((count - real_count) + i
					, wxString::Format(wxT("正在执行 %s 筛选中...[%zu/%zu]，已筛选出%zu个结果")
						, key, (count - real_count) + i + 1, count, container.size()), &skip);
				if (!cont) {
					return;
				}
				if (skip) {
					break;
				}
				zqdb::Msg msg(ZQDB_MSG_REQUEST_QUERY);
				msg.SetReqID(com::zqdb::proto::msg::MSG_REQUEST_QUERY_MD_SYSTEMSEL);
				req_data.clear_codes();
				for (size_t j = 0; j < 100 && (i + j) < real_count; j++) {
					zqdb::Code ycode(cur[i + j]);
					auto info = req_data.add_codes();
					info->set_exchange(ycode->Exchange);
					info->set_code(ycode->Code);
				}
				const auto& str = req_data.SerializeAsString();
				msg.SetData(str.data(), str.size());
				HNMSG rsp = nullptr;
				ZQDBRequest(msg, &rsp, 3000);
				if (rsp) {
					zqdb::Msg rsp_msg(rsp, zqdb::Msg::AutoDelete);
					if (rsp_msg.IsError()) {
						break;
					}
					com::zqdb::proto::msg::RspQrySystemSel rsp_data;
					size_t datalen = 0;
					const char* data = rsp_msg.GetData(&datalen);
					if (!rsp_data.ParseFromArray(data, datalen)) {
						break;
					}
					else {
						for (const auto& one : rsp_data.codes()) {
							auto h = ZQDBGetCode(one.code().c_str(), one.exchange().c_str());
							if (h) {
								container.AddResult(h);
							}
						}
					}
				}
				else {
					break;
				}
			}
		}
	}
	else {
		//本地选股
	}
	MyTechFrame::GetFrameByChild(this)->ShowContainer(key, container);
	dlg.Update(count
		, wxString::Format(wxT("完成 %s 筛选，共筛选出%zu个结果")
			, key, container.GetResultCount()));
}

void MyTechFrame::ShowSimilarity(const wxString& key, size_t num, int pos, zqdb::Windicator* view, size_t flags)
{
	auto cycle = tech_view_->GetCycle();
	auto cycleex = tech_view_->GetCycleEx();
	if (flags & SHOW_SIMILARITY_FLAG_CLOUD) {
		if (ZQDBIsClient() && !g_full && cycle >= CYC_DAY) {
			if (ZQDBGetUserPoints() < 200) {
				if (wxOK == wxMessageBox(wxString::Format(wxT("您不能使用%s\n请联系我们获取高级授权码"), key), wxT("提示"), wxOK | wxCANCEL)) {
					wxLaunchDefaultBrowser(wxT(PROJECT_CONTACT_URL));
				}
				return;
			}
		}
		else {
			wxMessageBox(wxString::Format(wxT("%s只支持日线及日线以上周期选股！！！"), key), wxT("提示"));
			return;
		}
	}
	bool market_value_flag[2] = { false, false };
	double market_value[2] = { 0, 0 };
	bool PE_flag[2] = { false, false };
	double PE[2] = { 0, 0 };
	int similarity = 80;
	{
		MySelectDlg dlg(this, true);
		if (wxID_OK != dlg.ShowModal()) {
			return;
		}
		market_value_flag[0] = dlg.GetMarketValueMin(market_value[0]);
		market_value_flag[1] = dlg.GetMarketValueMax(market_value[1]);
		PE_flag[0] = dlg.GetPEMin(PE[0]);
		PE_flag[1] = dlg.GetPEMax(PE[1]);
		similarity = dlg.GetSimilarity();
	}
	zqdb::AllCode cur; //默认筛选全部
	auto count = cur.size();
	std::multimap<int, HZQDB> results;
	wxProgressDialog dlg(wxT("筛选"),
		// "Reserve" enough space for the multiline
		// messages below, we'll change it anyhow
		// immediately in the loop below
		wxString(' ', 100) + "\n",
		count,    // range
		this,   // parent
		wxPD_CAN_ABORT |
		wxPD_CAN_SKIP |
		wxPD_APP_MODAL |
		//wxPD_AUTO_HIDE | // -- try this as well
		//wxPD_ELAPSED_TIME |
		//wxPD_ESTIMATED_TIME |
		//wxPD_REMAINING_TIME |
		wxPD_SMOOTH // - makes indeterminate mode bar on WinXP very small
	);
	bool cont = true;
	auto xdata = tech_view_->GetCalcData();
	auto xcount = xdata->GetDataCount();
	if (pos < 0) {
		//最近num
		if (num > xcount) {
			pos = 0;
			num = xcount;
		}
		else {
			pos = xcount - num;
		}
	}
	else if ((pos + num) >= xcount) {
		if (pos > xcount) {
			if (num > xcount) {
				pos = 0;
				num = xcount;
			}
			else {
				pos = xcount - num;
			}
		}
		else {
			num = xcount - pos;
		}
	}
	if (flags & SHOW_SIMILARITY_FLAG_CLOUD) {
		//云选股
		zqdb::Code code(xdata->GetCode());
		com::zqdb::proto::msg::ReqQrySimilarity req_data;
		req_data.set_exchange(code->Exchange);
		req_data.set_code(code->Code);
		req_data.set_period(cycle);
		req_data.set_periodex(cycleex);
		uint32_t date = 0, time = 0;
		xdata->GetDataTimeByPos(pos, &date, &time);
		req_data.set_date(date);
		req_data.set_time(time);
		req_data.set_count(num);
		bool request = true;
		if (!view || view->IsMajor()) {
			req_data.set_indicator("MA");
		}
		else {
			auto xind = view->GetIndicator(0);
			switch (xind->GetType())
			{
			case zqdb::Indicator::CALC: {
				req_data.set_indicator(xind->GetCalcName());
				//req_data.set_indicatorparam();
			} break;
			case zqdb::Indicator::VOLUME: {
				req_data.set_indicator("VOLUME");
			} break;
			case zqdb::Indicator::AMOUNT: {
				req_data.set_indicator("AMOUNT");
			} break;
			default: {
				request = false;
			} break;
			}
		}
		req_data.set_similarity(similarity);
		if (request) {
			for (size_t i = count; i > 0; i--)
			{
				zqdb::Code ycode(cur[i - 1]);
				zqdb::Product yproduct(ycode.GetProduct());
				if (yproduct->Type != PRODUCT_TYPE_Stock) {
					cur.erase(cur.begin() + (i - 1));
				}
				else {
					zqdb::CodeT<tagStockCodeInfo> ystock(ycode);
					if ((market_value_flag[0] && ystock->MarketValue < market_value[0])
						|| (market_value_flag[1] && ystock->MarketValue > market_value[1])
						|| (PE_flag[0] && ystock->PE_TTM < PE[0])
						|| (PE_flag[1] && ystock->PE_TTM > PE[1])) {
						cur.erase(cur.begin() + (i - 1));
					}
				}
			}
			size_t real_count = cur.size();
			for (size_t i = 0; i < real_count; i += 100)
			{
				bool skip = false;
				cont = dlg.Update((count - real_count) + i
					, wxString::Format(wxT("正在执行 %s 筛选中...[%zu/%zu]，已筛选出%zu个结果")
						, key, (count - real_count) + i + 1, count, results.size()), &skip);
				if (!cont) {
					return;
				}
				if (skip) {
					break;
				}
				zqdb::Msg msg(ZQDB_MSG_REQUEST_QUERY);
				msg.SetReqID(com::zqdb::proto::msg::MSG_REQUEST_QUERY_MD_SIMILARITY);
				req_data.clear_codes();
				for (size_t j = 0; j < 100 && (i + j) < real_count; j++) {
					zqdb::Code ycode(cur[i + j]);
					auto info = req_data.add_codes();
					info->set_exchange(ycode->Exchange);
					info->set_code(ycode->Code);
				}
				const auto& str = req_data.SerializeAsString();
				msg.SetData(str.data(), str.size());
				HNMSG rsp = nullptr;
				ZQDBRequest(msg, &rsp, 3000);
				if (rsp) {
					zqdb::Msg rsp_msg(rsp, zqdb::Msg::AutoDelete);
					if (rsp_msg.IsError()) {
						break;
					}
					com::zqdb::proto::msg::RspQrySimilarity rsp_data;
					size_t datalen = 0;
					const char* data = rsp_msg.GetData(&datalen);
					if (!rsp_data.ParseFromArray(data, datalen)) {
						break;
					}
					else {
						for (const auto& one : rsp_data.codes()) {
							auto h = ZQDBGetCode(one.code().c_str(), one.exchange().c_str());
							if (h) {
								results.insert(std::make_pair(one.similarity(), h));
							}
						}
					}
				}
				else {
					break;
				}
			}
		}
	}
	else {
		//本地选股
		for (size_t i = 0, j = ZQDBGetModuleCount(); i < j; i++)
		{
			auto id = ZQDBRefresh(ZQDBGetModuleAt(i), 0, ZQDB_REFRESH_FLAG_FIND_RESULT); //刷新行情
			size_t k = 150;
			do {
				if (ZQDBIsRefreshed(id)) {
					break;
				}
				bool skip = false;
				cont = dlg.Pulse(wxString::Format(wxT("正在准备 %s 筛选..."), key), &skip);
				if (!cont) {
					return;
				}
				if (skip) {
					break;
				}
				wxMilliSleep(100);
			} while (--k > 0);
			if (k == 0) {
				ZQDBRefreshed(id);
			}
		}
		auto xopen = (double*)xdata->GetData(mdb::Field(MDB_FIELD_INDEX(ZQDB, KDATA, OPEN)));
		auto xhigh = (double*)xdata->GetData(mdb::Field(MDB_FIELD_INDEX(ZQDB, KDATA, HIGH)));
		auto xlow = (double*)xdata->GetData(mdb::Field(MDB_FIELD_INDEX(ZQDB, KDATA, LOW)));
		auto xclose = (double*)xdata->GetData(mdb::Field(MDB_FIELD_INDEX(ZQDB, KDATA, CLOSE)));
		auto xbuffer = xclose;
		std::shared_ptr<zqdb::Calc::Indicator> xma5;
		zqdb::Indicator* xind = nullptr;
		if (!view || view->IsMajor()) {
			if (!tech_view_->IsTrend()) {
				xma5 = std::make_shared<zqdb::Calc::Indicator>("MA", CALC_MAJOR, *xdata, zqdb::Calc::InputAttr(R"({"N":5})", XUtil::XML_FLAG_JSON_STRING));
				xbuffer = (double*)xma5->GetResultValue(0);
			}
		}
		else {
			xind = view->GetIndicator(0);
			switch (xind->GetType())
			{
			case zqdb::Indicator::CALC: {
				xbuffer = (double*)xind->GetResultValue(0);
			} break;
			case zqdb::Indicator::VOLUME: {
				xbuffer = (double*)xdata->GetData(mdb::Field(MDB_FIELD_INDEX(ZQDB, KDATA, VOLUME)));
			} break;
			case zqdb::Indicator::AMOUNT: {
				xbuffer = (double*)xdata->GetData(mdb::Field(MDB_FIELD_INDEX(ZQDB, KDATA, AMOUNT)));
			} break;
			default: {
				xbuffer = nullptr;
			} break;
			}
		}
		if (xbuffer) {
			xopen += pos;
			xhigh += pos;
			xlow += pos;
			xclose += pos;
			xbuffer += pos;
			for (size_t i = 0; i < count; i++)
			{
				bool skip = false;
				cont = dlg.Update(i
					, wxString::Format(wxT("正在执行 %s 筛选中...[%zu/%zu]，已筛选出%zu个结果")
						, key, i + 1, count, results.size()), &skip);
				if (!cont) {
					return;
				}
				if (skip) {
					break;
				}
				zqdb::Code ycode(cur[i]);
				zqdb::Product yproduct(ycode.GetProduct());
				if (yproduct->Type != PRODUCT_TYPE_Stock) {
					//continue;
				}
				else {
					zqdb::CodeT<tagStockCodeInfo> ystock(ycode);
					if ((market_value_flag[0] && ystock->MarketValue < market_value[0])
						|| (market_value_flag[1] && ystock->MarketValue > market_value[1])
						|| (PE_flag[0] && ystock->PE_TTM < PE[0])
						|| (PE_flag[1] && ystock->PE_TTM > PE[1])) {
						continue;
					}
				}
				zqdb::Calc::Data ydata(ycode, cycle, cycleex);
				auto ycount = ydata.GetDataCount();
				if (num < ycount) {
					auto yopen = (double*)ydata.GetData(mdb::Field(MDB_FIELD_INDEX(ZQDB, KDATA, OPEN)));
					auto yhigh = (double*)ydata.GetData(mdb::Field(MDB_FIELD_INDEX(ZQDB, KDATA, HIGH)));
					auto ylow = (double*)ydata.GetData(mdb::Field(MDB_FIELD_INDEX(ZQDB, KDATA, LOW)));
					auto yclose = (double*)ydata.GetData(mdb::Field(MDB_FIELD_INDEX(ZQDB, KDATA, CLOSE)));
					auto ybuffer = yclose;
					std::shared_ptr<zqdb::Calc::Indicator> yma5;
					std::shared_ptr<zqdb::Calc::Indicator> yind;
					if (!view || view->IsMajor()) {
						if (!tech_view_->IsTrend()) {
							yma5 = std::make_shared<zqdb::Calc::Indicator>("MA", CALC_MAJOR, ydata, zqdb::Calc::InputAttr(R"({"N":5})", XUtil::XML_FLAG_JSON_STRING));
							ybuffer = (double*)yma5->GetResultValue(0);
						}
					}
					else {
						switch (xind->GetType())
						{
						case zqdb::Indicator::CALC: {
							auto input = xind->GetCalcInput();
							yind = std::make_shared<zqdb::Calc::Indicator>(xind->GetCalcName(), xind->GetCalcType(), ydata
								, input ? *input : nullptr);
							ybuffer = (double*)yind->GetResultValue(0);
						} break;
						case zqdb::Indicator::VOLUME: {
							ybuffer = (double*)ydata.GetData(mdb::Field(MDB_FIELD_INDEX(ZQDB, KDATA, VOLUME)));
						} break;
						case zqdb::Indicator::AMOUNT: {
							ybuffer = (double*)ydata.GetData(mdb::Field(MDB_FIELD_INDEX(ZQDB, KDATA, AMOUNT)));
						} break;
						default: {
							ybuffer = nullptr;
						} break;
						}
					}
					if (ybuffer) {
						auto ypos = (ycount - num);
						yopen += ypos;
						yhigh += ypos;
						ylow += ypos;
						yclose += ypos;
						ybuffer += ypos;
						//
						auto sim = ZQDBCalcSimilarity(xbuffer, ybuffer, num) * 100;
						if (sim > similarity) {
							results.insert(std::make_pair(sim, ycode));
						}
					}
				}
			}
		}
	}
	zqdb::Calc::StdContainer container;
	for (auto it = results.rbegin(); it != results.rend(); ++it)
	{
		container.AddResult(it->second);
	}
	wxASSERT(container.GetResultCount() == results.size());
	MyTechFrame::GetFrameByChild(this)->ShowContainer(key, container);
	dlg.Update(count
		, wxString::Format(wxT("完成 %s 筛选，共筛选出%zu个结果")
			, key, container.GetResultCount()));
}

void MyTechFrame::RestoreFilter(const wxString& key, size_t pos)
{
	/*wxString label2;
	if (label.IsEmpty()) {
		zqdb::Calc::Func func(all_filter_func_[pos]);
		char buf[260] = { 0 };
		auto name = func.GetAttrAsStr("name", buf, 260);
		label2 = utf2wxString(name[0] ? name : func.GetCalcName());
	}
	filter_bar_->DeleteButton(ID_FILTER + pos);
	filter_bar_->InsertHybridButton(1 + pos, ID_FILTER + pos
		, label.IsEmpty() ? label2 : label, skin_info_ptr_->GetBitmap32(wxT("筛选")));
	filter_bar_->SetButtonMaxSizeClass(ID_FILTER + pos, wxRIBBON_BUTTONBAR_BUTTON_LARGE);
	filter_bar_->SetButtonMinSizeClass(ID_FILTER + pos, wxRIBBON_BUTTONBAR_BUTTON_LARGE);
	cur_filter_func_ = -1;
	if (!label.IsEmpty()) {
		m_ribbon->Realize();
	}*/
}

void MyTechFrame::UpdateFilter(const wxString& key, size_t pos)
{
	/*ASSERT(pos < all_filter_func_.size());
	if (cur_filter_func_ >= 0 && cur_filter_func_ < pos) {
		RestoreFilter(cur_filter_func_);
	}
	filter_bar_->DeleteButton(ID_FILTER + pos);
	filter_bar_->InsertToggleButton(1 + pos, ID_FILTER + pos, label, skin_info_ptr_->GetBitmap32(wxT("筛选")));
	filter_bar_->SetButtonMaxSizeClass(ID_FILTER + pos, wxRIBBON_BUTTONBAR_BUTTON_LARGE);
	filter_bar_->SetButtonMinSizeClass(ID_FILTER + pos, wxRIBBON_BUTTONBAR_BUTTON_LARGE);
	filter_bar_->ToggleButton(ID_FILTER + pos, true);
	if (cur_filter_func_ >= 0 && cur_filter_func_ > pos) {
		RestoreFilter(cur_filter_func_);
	}
	cur_filter_func_ = pos;
	m_ribbon->Realize();*/
}

void MyTechFrame::ShowFilter(const wxString& key, zqdb::Calc::Filter& filter)
{
#if 0
	auto count = code_view_->Count();
	if (count <= 0) {
		wxMessageBox(wxT("列表为空，无需筛选！！！"), wxT("筛选"));
		return;
	}
	auto cur = code_view_->Cur();
	ASSERT(count == cur.GetResultCount());
#else
	zqdb::AllCode cur; //默认筛选全部
	auto count = cur.size();
#endif//
	std::multimap<int, HZQDB> results;
	wxProgressDialog dlg(wxT("筛选"),
		// "Reserve" enough space for the multiline
		// messages below, we'll change it anyhow
		// immediately in the loop below
		wxString(' ', 100) + "\n",
		count,    // range
		this,   // parent
		wxPD_CAN_ABORT |
		wxPD_CAN_SKIP |
		wxPD_APP_MODAL |
		//wxPD_AUTO_HIDE | // -- try this as well
		//wxPD_ELAPSED_TIME |
		//wxPD_ESTIMATED_TIME |
		//wxPD_REMAINING_TIME |
		wxPD_SMOOTH // - makes indeterminate mode bar on WinXP very small
	);
	bool cont = true;
	for (size_t i = 0, j = ZQDBGetModuleCount(); i < j; i++)
	{
		auto id = ZQDBRefresh(ZQDBGetModuleAt(i), 0, ZQDB_REFRESH_FLAG_FIND_RESULT); //刷新行情
		size_t k = 150;
		do {
			if (ZQDBIsRefreshed(id)) {
				break;
			}
			bool skip = false;
			cont = dlg.Pulse(wxString::Format(wxT("正在准备 %s 筛选..."), key), &skip);
			if (!cont) {
				return;
			}
			if (skip) {
				break;
			}
			wxMilliSleep(100);
		} while (--k > 0);
		if (k == 0) {
			ZQDBRefreshed(id);
		}
	}
	for (size_t i = 0; i < count; i++)
	{
		bool skip = false;
		cont = dlg.Update(i
			, wxString::Format(wxT("正在执行 %s 筛选中...[%zu/%zu]，已筛选出%zu个结果")
				, key, i + 1, count, results.size()), &skip);
		if (!cont) {
			return;
		}
		if (skip) {
			break;
		}
		auto h = cur[i];
		auto rlt = filter.Calc(h);
		if (rlt) {
			results.insert(std::make_pair(rlt,h));
		}
	}
	zqdb::Calc::StdContainer container;
	for (auto it = results.rbegin(); it != results.rend(); ++it)
	{
		container.AddResult(it->second);
	}
	wxASSERT(container.GetResultCount() == results.size());
	ShowContainer(key, container);
	dlg.Update(count
		, wxString::Format(wxT("完成 %s 筛选，共筛选出%zu个结果")
			, key, container.GetResultCount()));
}

void MyTechFrame::OnFilter(wxRibbonButtonBarEvent& evt)
{
	size_t pos = evt.GetId() - ID_FILTER;
	if (pos < all_filter_func_.size()) {
		zqdb::Calc::Func func(all_filter_func_[pos]);
		char buf[260] = { 0 };
		auto name = func.GetAttrAsStr("name", buf, 260);
		auto label = utf2wxString(name[0] ? name : func.GetCalcName());
		/*if (code_view_->IsFilter() && pos == cur_filter_func_) {
			//取消筛选
			code_view_->ClearFilter();
			RestoreFilter(pos, label);
		}
		else*/ {
			//设置新筛选
			HZQDB data = *tech_view_->GetCalcData();
			zqdb::Calc::Filter filter(func.GetCalcName(), data, CYC_MAX, 0, nullptr);
			//code_view_->SetFilter(filter); 
			ShowFilter(label, filter);
			UpdateFilter(label, pos);
		}
	}
	else {
		/*if (code_view_->IsFilter()) {
			wxMessageBox(wxT("筛选中...，不支持搜索，请先取消筛选！"), wxT("提示"));
		}
		else*/ {
			//wxGetApp().GetSmartKBDlg()->ShowFor(this);
		}
	}
}

void MyTechFrame::OnFilterDropdown(wxRibbonButtonBarEvent& evt)
{
	size_t pos = evt.GetId() - ID_FILTER;
	if (pos >= all_filter_func_.size()) {
		return;
	}
	wxRibbonButtonBar* pBar = evt.GetBar();
	if (!pBar) {
		return;
	}
	zqdb::Calc::Func func(all_filter_func_[pos]);
	if (func.HasAttr("menu")) {
		char buf[4096] = { 0 };
		boost::property_tree::ptree cfg;
		XUtil::json_from_str(func.GetAttrAsStrEx("", buf, 4096), cfg);
		auto opt_menu = cfg.get_child_optional("menu");
		if (!opt_menu) {
			return;
		}
		auto& cfg_menu = opt_menu.get();
		if (cfg_menu.empty()) {
			return;
		}
		wxMenu menu;
		wxGetApp().ClearMenuMap();
		size_t id = ID_FILTER_MENU;
		for (auto it = cfg_menu.begin(); it != cfg_menu.end(); ++id, ++it)
		{
			auto name = it->second.get<std::string>("name");
			if (name.empty()) {
				menu.AppendSeparator();
			}
			else {
				menu.Append(id, utf2wxString(name.c_str()));
				wxGetApp().SetMenuData(id, &cfg);
			}
		}
		menu.AppendSeparator();
		auto menu_item = menu.Append(ID_CALC_EDIT_FILTER + pos, wxT("修改代码…"));
		zqdb::SetMenuItemClientData(menu_item, func);
		wxGetApp().SetMenuData(ID_FILTER_MENU_MAX, (void*)pos);
		evt.PopupMenu(&menu);
	}
}

void MyTechFrame::OnFilterGoto(wxCommandEvent& evt)
{
	if (evt.GetId() >= ID_FILTER_MENU && evt.GetId() < ID_FILTER_MENU_MAX) {
		auto pos = (size_t)wxGetApp().GetMenuData(ID_FILTER_MENU_MAX);
		if (pos >= all_filter_func_.size()) {
			return;
		}
		auto p_cfg = (boost::property_tree::ptree*)wxGetApp().GetMenuData(evt.GetId());
		if (p_cfg) {
			auto& cfg = *p_cfg;
			auto opt_menu = cfg.get_child_optional("menu");
			if (!opt_menu) {
				return;
			}
			auto& cfg_menu = opt_menu.get();
			if (cfg_menu.empty()) {
				return;
			}
			size_t id = ID_FILTER_MENU;
			for (auto it = cfg_menu.begin(); it != cfg_menu.end(); ++id, ++it)
			{
				if (evt.GetId() == id) {
					zqdb::Calc::Func func(all_filter_func_[pos]);
					auto name = it->second.get<std::string>("name");
					wxString label = utf2wxString(name.c_str());
					HZQDB data = *tech_view_->GetCalcData();
					std::string str;
					XUtil::json_to_str(str, it->second.get_child("input"));
					zqdb::Calc::InputAttr input(str.c_str(), XUtil::XML_FLAG_JSON_STRING);
					zqdb::Calc::Filter filter(func.GetCalcName(), data, CYC_MAX, 0, input);
					//code_view_->SetFilter(filter);
					ShowFilter(label, filter);
					UpdateFilter(label, pos);
					break;
				}
			}
		}
	}
}

void MyTechFrame::OnFilterSetting(wxRibbonPanelEvent& evt)
{
	if (cur_filter_func_ < all_filter_func_.size()) {
		zqdb::Calc::Func func(all_filter_func_[cur_filter_func_]);
		CalcFuncDlg dlg(this, CALC_FILTER, func.GetCalcName());
		dlg.ShowModal();
	}
}

#ifdef _SORT

void MyTechFrame::Sort(int id)
{
	MY_CODE_SORT_TYPE old_type = SORT_ZDF;
	size_t old_secs = 0;
	int old_sort = IsSort(&old_type, &old_secs);

	MY_CODE_SORT_TYPE type = SORT_ZDF;
	int sort = 0;
	if (id < ID_SORT_CALC) {
		switch (id)
		{
		case ID_SORT: {
		} break;
		default: {
			type = SORT_ZDS;
		} break;
		}
	}
	else {
		type = SORT_CALC_SORT;
	}

	if (id < ID_SORT_CALC) {
		size_t secs = 0;
		switch (type)
		{
		case SORT_ZDF:
			break;
		case SORT_ZDS:
			secs = wxGetApp().GetSortQuick();
			break;
		}
		if (type != old_type) {
			sort = 0;
		}
		else {
			if (secs != old_secs) {
				sort = 0;
			}
			else {
				sort = old_sort;
			}
		}
		switch (sort)
		{
		case -1:
			sort = 0;
			break;
		case 0:
			sort = 1;
			break;
		case 1:
			sort = -1;
			break;
		}
		SortByZD(type, secs, sort);
	}
	else {
		size_t pos = id - ID_SORT_CALC;
		if (type != old_type) {
			sort = 0;
		}
		else if (pos != cur_sort_func_) {
			sort = 0;
			InnerSetCurSort(pos);
		}
		else {
			sort = old_sort;
		}
		zqdb::Calc::Func func(all_sort_func_[pos]);
		switch (sort)
		{
		case -1:
			sort = 0;
			break;
		case 0:
			sort = 1;
			break;
		case 1:
			sort = -1;
			break;
		}
		zqdb::Calc::Sort calc(func.GetCalcName(), nullptr);
		SortByCalc(calc, sort);
	}
	InnerUpdateSort();
	m_ribbon->Realize();
}

void MyTechFrame::OnSort(wxRibbonButtonBarEvent& evt)
{
	wxRibbonButtonBar* pBar = evt.GetBar();
	if (pBar) {
		Sort(evt.GetId());
	}
}

void MyTechFrame::OnSortDropdown(wxRibbonButtonBarEvent& evt)
{
	wxRibbonButtonBar* pBar = evt.GetBar();
	if (pBar) {
		wxMenu menu;
		switch (evt.GetId()) 
		{
		case ID_SORT: {
			//
		} break;
		case ID_SORT_QUICK: {
			menu.Append(ID_SORT_QUICK + 1, wxT("1分钟涨跌"));
			menu.Append(ID_SORT_QUICK + 2, wxT("2分钟涨跌"));
			menu.Append(ID_SORT_QUICK + 3, wxT("3分钟涨跌"));
			menu.Append(ID_SORT_QUICK + 4, wxT("4分钟涨跌"));
			menu.Append(ID_SORT_QUICK + 5, wxT("5分钟涨跌"));
		} break;
		default: {
			wxGetApp().ClearMenuMap();
			bool test = false;
			for (size_t i = 0, j = all_sort_func_.size(); i < j; i++)
			{
				zqdb::Calc::Func func(all_sort_func_[i]);
				if (i == cur_sort_func_) {
					continue;
				}
				else {
					char buf[260] = { 0 };
					auto type = func.GetAttrAsStr("type", buf, 260);
					if (strcmp(type, "test") == 0) {
						test = true;
						continue;
					}
					auto name = func.GetAttrAsStr("name", buf, 260);
					wxString label = utf2wxString(name[0] ? name : func.GetCalcName());
					auto menu_item = menu.Append(ID_SORT_CALC + i, label);
				}
				wxGetApp().SetMenuData(ID_SORT_CALC + i, all_sort_func_[i]);
			}
			if (test) {
				menu.AppendSeparator();
				for (size_t i = 0, j = all_sort_func_.size(); i < j; i++)
				{
					zqdb::Calc::Func func(all_sort_func_[i]);
					if (i == cur_sort_func_) {
						continue;
					}
					else {
						char buf[260] = { 0 };
						auto type = func.GetAttrAsStr("type", buf, 260);
						if (strcmp(type, "test") != 0) {
							continue;
						}
						auto name = func.GetAttrAsStr("name", buf, 260);
						wxString label = utf2wxString(name[0] ? name : func.GetCalcName());
						auto menu_item = menu.Append(ID_SORT_CALC + i, label);
					}
					wxGetApp().SetMenuData(ID_SORT_CALC + i, all_sort_func_[i]);
				}
			}
		} break;
		}
		evt.PopupMenu(&menu);
	}
}

void MyTechFrame::OnSortGoto(wxCommandEvent& evt)
{
	if (evt.GetId() > ID_SORT_QUICK && evt.GetId() <= ID_SORT_QUICK_MAX) {
		size_t secs = 0;
		switch (evt.GetId()) 
		{
		case ID_SORT_QUICK + 1: {
			secs = 60;
		} break;
		case ID_SORT_QUICK + 2: {
			secs = 120;
		} break;
		case ID_SORT_QUICK + 3: {
			secs = 180;
		} break;
		case ID_SORT_QUICK + 4: {
			secs = 240;
		} break;
		case ID_SORT_QUICK + 5: {
			secs = 300;
		} break;
		default: {
			wxASSERT(0);
		} break;
		}
		wxGetApp().SetSortQuick(secs);
	}
	Sort(evt.GetId());
}

void MyTechFrame::OnSortSetting(wxRibbonPanelEvent& evt)
{
	if (cur_sort_func_ < all_sort_func_.size()) {
		zqdb::Calc::Func func(all_sort_func_[cur_sort_func_]);
		CalcFuncDlg dlg(this, CALC_SORT, func.GetCalcName());
		dlg.ShowModal();
	}
}

#endif

void MyTechFrame::SetTechKLine(CALC_KLINE_TYPE type)
{
	if (!tech_view_) {
		return;
	}
	if (!tech_bar_) {
		return;
	}
	for (size_t i = CALC_KLINE_K; i < CALC_KLINE_TYPE_MAX; i++) 
	{
		if (type == (CALC_KLINE_TYPE)i) {
			tech_bar_->ToggleTool(ID_TECHVIEW_KLINE + i, true);
		}
		else {
			tech_bar_->ToggleTool(ID_TECHVIEW_KLINE + i, false);
		}
	}
	switch (type)
	{
	case CALC_KLINE_TREND: {
		tech_view_->SetKType(CALC_KLINE_TREND, CALC_STICK_LINE);
	} break;
	case CALC_KLINE_BAR: {
		tech_view_->SetKType(CALC_KLINE_BAR, CALC_STICK_LINE);
	} break;
	default: {
		tech_view_->SetKType(CALC_KLINE_K, CALC_STICK_BAR);
	} break;
	}
	wxGetApp().SetTechKType(type);
}

void MyTechFrame::OnTechKLine(wxRibbonToolBarEvent& evt)
{
	CALC_KLINE_TYPE type = (CALC_KLINE_TYPE)(evt.GetId() - ID_TECHVIEW_KLINE);
	switch (type)
	{
	/*case CALC_KLINE_TREND: {
		tech_bar_->ToggleTool(ID_TECHVIEW_KLINE + CALC_KLINE_TREND, false);
		wxMessageBox(wxT("暂不支持该功能，敬请期待..."), wxT("提示"));
		//tech_view_->SetKType(CALC_KLINE_TREND, CALC_STICK_LINE);
	} break;*/
	default: {
		SetTechKLine(type);
	} break;
	}
}

void MyTechFrame::SetTechCycle(PERIODTYPE cycle_pos)
{
	if (!tech_view_) {
		return;
	}
	if (!tech_bar_) {
		return;
	}
	size_t cycleex = 0;
	auto cycle = cycle_pos;
	switch (cycle)
	{
	case CYC_TICK: {
		cycle = CYC_1MIN;
		cycleex = 1;
	} break;
#if USE_CYC_SEC
	case CYC_ANYSEC: {
		cycleex = wxGetApp().GetTechCycleAnySec();
	} break;
#endif
	case CYC_ANYMIN: {
		cycleex = wxGetApp().GetTechCycleAnyMin();
	} break;
	}
	auto old_cycle = tech_view_->GetCycle();
	auto old_cycleex = tech_view_->GetCycleEx();
	auto old_cycle_pos = (old_cycle == CYC_1MIN && old_cycleex) ? CYC_TICK : old_cycle;
	if (old_cycle_pos == cycle_pos) {
		tech_bar_->ToggleTool(ID_TECHVIEW_CYCLE + cycle_pos, true);
	}
	else {
		//切换周期时，先移除所有划线，后期需要做提示是否保存划线
		auto major = tech_view_->GetMajorView();
		if (major) {
			major->RemoveAllDrawline();
		}
		if (tech_bar_->FindById(ID_TECHVIEW_CYCLE + old_cycle_pos)) {
			tech_bar_->ToggleTool(ID_TECHVIEW_CYCLE + old_cycle_pos, false);
		}
		else {
			//
		}
		if (tech_bar_->FindById(ID_TECHVIEW_CYCLE + cycle_pos)) {
			tech_bar_->ToggleTool(ID_TECHVIEW_CYCLE + cycle_pos, true);
		}
		else {
			//
		}
		tech_view_->SetCycle(cycle, cycleex);
		wxGetApp().SetTechCycleCur(cycle);
	}
}

void MyTechFrame::OnTechCycle(wxRibbonToolBarEvent& evt)
{
	SetTechCycle((PERIODTYPE)(evt.GetId() - ID_TECHVIEW_CYCLE));
}

void MyTechFrame::OnTechCycleDropdown(wxRibbonToolBarEvent& evt)
{
	if (!tech_view_) {
		return;
	}
	wxRibbonToolBar* pBar = evt.GetBar();
	if (!pBar) {
		return;
	}

	wxMenu menu;
	wxGetApp().ClearMenuMap();
	menu.AppendCheckItem(ID_TECHVIEW_CYCLE + CYC_TICK, wxT("分时"));
#if USE_CYC_SEC
	menu.AppendCheckItem(ID_TECHVIEW_CYCLE + CYC_5SEC, wxT("5秒"));
	size_t anysec = wxGetApp().GetTechCycleAnySec();
	menu.AppendCheckItem(ID_TECHVIEW_CYCLE + CYC_ANYSEC, wxString::Format(wxT("%zu秒"), anysec));
#endif
	size_t anymin = wxGetApp().GetTechCycleAnyMin();
	menu.AppendCheckItem(ID_TECHVIEW_CYCLE + CYC_1MIN, wxT("1分"));
	size_t min = 1, premin = 1;
	for (size_t cycle = CYC_5MIN; cycle < CYC_DAY; cycle++)
	{
		premin = min;
		switch (cycle)
		{
		case CYC_5MIN: {
			min = 5;
		} break;
		case CYC_15MIN: {
			min = 15;
		} break;
		case CYC_30MIN: {
			min = 30;
		} break;
		case CYC_60MIN: {
			min = 60;
		} break;
		}
		if (anymin > premin && anymin < min) {
			menu.AppendCheckItem(ID_TECHVIEW_CYCLE + CYC_ANYMIN, wxString::Format(wxT("%zu分"), anymin));
		}
		switch (cycle)
		{
		case CYC_5MIN: {
			menu.AppendCheckItem(ID_TECHVIEW_CYCLE + CYC_5MIN, wxT("5分"));
		} break;
		case CYC_15MIN: {
			menu.AppendCheckItem(ID_TECHVIEW_CYCLE + CYC_15MIN, wxT("15分"));
		} break;
		case CYC_30MIN: {
			menu.AppendCheckItem(ID_TECHVIEW_CYCLE + CYC_30MIN, wxT("30分"));
		} break;
		case CYC_60MIN: {
			menu.AppendCheckItem(ID_TECHVIEW_CYCLE + CYC_60MIN, wxT("60分"));
		} break;
		}
	}
	menu.AppendCheckItem(ID_TECHVIEW_CYCLE + CYC_DAY, wxT("日K"));
	menu.AppendCheckItem(ID_TECHVIEW_CYCLE + CYC_WEEK, wxT("周K"));
	menu.AppendCheckItem(ID_TECHVIEW_CYCLE + CYC_MONTH, wxT("月K"));
	menu.AppendCheckItem(ID_TECHVIEW_CYCLE + CYC_SEASON, wxT("季K"));
	menu.AppendCheckItem(ID_TECHVIEW_CYCLE + CYC_YEAR, wxT("年K"));
	menu.Check(ID_TECHVIEW_CYCLE + (int)tech_view_->GetCycle(), true);
	evt.PopupMenu(&menu);
}

void MyTechFrame::OnTechCycleGoto(wxCommandEvent& evt)
{
	auto cycle = (PERIODTYPE)(evt.GetId() - ID_TECHVIEW_CYCLE);
	bool relayout = !tech_bar_->FindById(ID_TECHVIEW_CYCLE + cycle);
	if (relayout) {
		for (int i = cycle - 1; i > CYC_TICK; i--)
		{
			auto pos = tech_bar_->GetToolPos(ID_TECHVIEW_CYCLE + i);
			if (pos >= 0) {
				wxString str = ZQDBCycle2Str(cycle);
				tech_bar_->InsertToggleTool(pos + 1, ID_TECHVIEW_CYCLE + cycle, skin_info_ptr_->GetBitmap16(str));
				break;
			}
		}
	}
	SetTechCycle(cycle);
	if (relayout) {
		m_ribbon->Realize();
	}
}

void MyTechFrame::OnTechMove(wxRibbonToolBarEvent& evt)
{
	wxMessageBox(wxT("暂不支持该功能，敬请期待..."), wxT("提示"));
}

void MyTechFrame::DoDrawline(CALC_DRAWLINE_TYPE type)
{
	if (!tech_view_) {
		return;
	}
	auto major = tech_view_->GetMajorView();
	if (!major) {
		return;
	}
	size_t pos = 0;
	auto obj = major->IsInDrawline(&pos);
	if (obj) {
		//画线中就先取消画线
		major->RemoveIndicator(pos);
		major->Refresh();
	}
	auto name = ZQDBGetDrawlineName(type);
	if (!name || !name[0]) {
		return;
	}
	char xml[1024] = { 0 };
	sprintf(xml, R"({"name":"%s"})", name);
	major->AppendDrawline(name, xml, XUtil::XML_FLAG_JSON_STRING);
}

void MyTechFrame::OnTechDrawline(wxRibbonToolBarEvent& evt)
{
	DoDrawline((CALC_DRAWLINE_TYPE)(evt.GetId() - ID_TECHVIEW_DRAWLINE));
}

void MyTechFrame::OnTechDrawlineDropdown(wxRibbonToolBarEvent& evt)
{
	if (!tech_view_) {
		return;
	}
	wxRibbonToolBar* pBar = evt.GetBar();
	if (!pBar) {
		return;
	}
	wxMenu menu;
	wxGetApp().ClearMenuMap();
	menu.AppendCheckItem(ID_TECHVIEW_DRAWLINE + CALC_DRAWLINE_DDLINE, wxT("直线"));
	menu.AppendCheckItem(ID_TECHVIEW_DRAWLINE + CALC_DRAWLINE_DOTDOT, wxT("线段"));
	menu.AppendCheckItem(ID_TECHVIEW_DRAWLINE + CALC_DRAWLINE_PLINE, wxT("平行线"));
	menu.AppendCheckItem(ID_TECHVIEW_DRAWLINE + CALC_DRAWLINE_REGRESSCHANNEL, wxT("通道线"));
	menu.AppendCheckItem(ID_TECHVIEW_DRAWLINE + CALC_DRAWLINE_CYCLELINE, wxT("周期线"));
	menu.AppendCheckItem(ID_TECHVIEW_DRAWLINE + CALC_DRAWLINE_FABCYCLELINE, wxT("斐波那契周期线"));
	menu.AppendCheckItem(ID_TECHVIEW_DRAWLINE + CALC_DRAWLINE_GOLDLINE, wxT("黄金分割线"));
	//menu.AppendCheckItem(ID_TECHVIEW_DRAWLINE + CALC_DRAWLINE_TEXT, wxT("文字"));
	//menu.AppendCheckItem(ID_TECHVIEW_DRAWLINE + CALC_DRAWLINE_UPARROW, wxT("上箭头"));
	//menu.AppendCheckItem(ID_TECHVIEW_DRAWLINE + CALC_DRAWLINE_DOWNARROW, wxT("下箭头"));
	evt.PopupMenu(&menu);
}

void MyTechFrame::OnTechDrawlineGoto(wxCommandEvent& evt)
{
	DoDrawline((CALC_DRAWLINE_TYPE)(evt.GetId() - ID_TECHVIEW_DRAWLINE));
}

void MyTechFrame::OnTechZoom(wxRibbonToolBarEvent& evt)
{
	if (!tech_view_) {
		return;
	}
	switch (evt.GetId())
	{
	case ID_TECHVIEW_ZOOM_IN: {
		tech_view_->Scale(-1);
	} break;
	case ID_TECHVIEW_ZOOM_OUT: {
		tech_view_->Scale(1);
	} break;
	}
}

void MyTechFrame::OnTechViewDropdown(wxRibbonToolBarEvent& evt)
{
	if (!tech_view_) {
		return;
	}
	wxRibbonToolBar* pBar = evt.GetBar();
	if (!pBar) {
		return;
	}
	wxMenu menu;
	wxGetApp().ClearMenuMap();
	menu.AppendCheckItem(ID_TECHVIEW_VIEW + 1, wxT("2图"));
	menu.AppendCheckItem(ID_TECHVIEW_VIEW + 2, wxT("3图"));
	menu.AppendCheckItem(ID_TECHVIEW_VIEW + 3, wxT("4图"));
	menu.AppendCheckItem(ID_TECHVIEW_VIEW + 4, wxT("5图"));
	menu.AppendCheckItem(ID_TECHVIEW_VIEW + 5, wxT("6图"));
	menu.AppendCheckItem(ID_TECHVIEW_VIEW + 6, wxT("7图"));
	menu.AppendCheckItem(ID_TECHVIEW_VIEW + 7, wxT("8图"));
	menu.AppendCheckItem(ID_TECHVIEW_VIEW + 8, wxT("9图"));
	evt.PopupMenu(&menu);
}

void MyTechFrame::OnTechViewGoto(wxCommandEvent& evt)
{
	if (!tech_view_) {
		return;
	}
	auto new_count = evt.GetId() - ID_TECHVIEW_VIEW + 1;
	//这里注意技术画面还有一个底部坐标视图
	auto bottom_count = 1;
	auto old_count = tech_view_->GetViewCount() - bottom_count;
	if (new_count < old_count) {
		for (auto i = old_count; i > new_count; i--)
		{
			tech_view_->RemoveView(i - 1);
		}
	}
	else {
		auto view = tech_view_->GetView(old_count - 1);
		for (auto i = old_count; i < new_count; i++)
		{
			view = tech_view_->InsertView(view, R"(
      {
        "indicator": [
          {
            "name": "MACD"
          }
        ]
      })", XUtil::XML_FLAG_JSON_STRING);
		}
	}
	tech_bar_->SetToolNormalBitmap(ID_TECHVIEW_VIEW, skin_info_ptr_->GetBitmap16(wxString::Format(wxT("%d图"), new_count)));
}

inline static const char* emweb_get_exchange(const char* exchange) {
	if (strcmp(exchange, EXCHANGE_SZSE) == 0) {
		return "sz";
	}
	else if (strcmp(exchange, EXCHANGE_SSE) == 0) {
		return "sh";
	}
	else if (strcmp(exchange, EXCHANGE_BSE) == 0) {
		return "bj";
	}
	return nullptr;
}

void MyTechFrame::ShowF10(bool update)
{
	auto h = Get();
	if (!h) {
		return;
	}
	zqdb::Code code(h);
	if (f10_frame_ || !update) {
		zqdb::Product product(code.GetProduct());
		if (product->Type == PRODUCT_TYPE_Stock) {
			auto exchange = emweb_get_exchange(code->Exchange);
			if (exchange) {
				wxString key = code->Code;
				if (!f10_frame_) {
					auto url =
						//wxString::Format(wxT("https://emweb.eastmoney.com/pc_hsf10/pages/index.html?type=soft&code=%s%s&color=b#/cpbd")
						//	, exchange, code->Code)
						wxString::Format(wxT("http://basic.10jqka.com.cn/%s/")
							, key);
					std::ostringstream oss;
					oss << "{"
						<< R"("url":")" << wxString2utf(url).c_str() << R"(")"
						<< "}";
					f10_frame_ = new MyF10Frame(oss.str().c_str(), XUtil::XML_FLAG_JSON_STRING, this);
				}
				else {
					auto url = f10_frame_->GetURL();
					url.Replace(((MyF10Frame*)f10_frame_)->key, key);
					f10_frame_->LoadURL(url);
				}
				((MyF10Frame*)f10_frame_)->key = key;
				f10_frame_->Show();
				f10_frame_->Raise();
				return;
			}
		}
	}
	if (!update) {
		wxMessageBox(wxString::Format(wxT("暂不提供%s的F10信息，敬请期待..."), utf2wxString(code->Name)), wxT("提示"));
	}
}

void MyTechFrame::OnF10(wxRibbonToolBarEvent& evt)
{
	ShowF10();
}

void MyTechFrame::OnTechSetting(wxRibbonPanelEvent& evt)
{
	zqdb::TechDlg dlg(this);
	dlg.ShowModal();
}

void MyTechFrame::RunScript(zqdb::Calc::Func& func, zqdb::Calc::InputAttr& input)
{
	auto huser = GetUser();
	if (!huser) {
		/*wxMessageBox(wxT("没有登录交易账户!!!\n")
			wxT("请登录交易"), wxT("提示"), wxOK | wxCANCEL);
		return;*/
	}
	size_t target = zqdb::GetCalcTarget(func);
	HZQDB data = nullptr;
	PERIODTYPE cycle = zqdb::GetCalcCycle(func);
	size_t cycleex = 0;
	if (cycle == 0) {
		cycle = tech_view_->GetCycle();
		cycleex = tech_view_->GetCycleEx();
	}
	else {
		cycleex = zqdb::GetCalcCycleEx(func);
	}
	switch (target)
	{
	case 0: {
		//当前代码
		//data = Get();
		data = *tech_view_->GetCalcData();
	} break;
	case 1: {
		//当前列表
		data = code_view_->Cur();
	} break;
	default: {
		wxASSERT(0);
	} break;
	}
	zqdb::Calc::Script script(func.GetCalcName(), CALC_SCRIPT, data, cycle, cycleex, input, huser ? &huser : nullptr, huser ? 1 : 0);
	script.Close();
}

void MyTechFrame::OnScript(wxRibbonButtonBarEvent& evt)
{
	//直接执行
	size_t pos = evt.GetId() - ID_SCRIPT;
	if (pos < all_script_func_.size()) {
		zqdb::Calc::Func func(all_script_func_[pos]);
		auto name = func.GetCalcName();
		auto input = zqdb::GetCalcInputAttr(func);
		/*if (strcmp(name, "OrderOpen") == 0) {
			if (!input) {
				char buf[260] = { 0 };
				sprintf(buf, R"({"volume":%.0f})", zqdb::CalcTradeOpenVolume(Get()));
				input = zqdb::Calc::InputAttr(buf, XUtil::XML_FLAG_JSON_STRING);
			}
			else {
				auto volume = input.GetAttrAsDouble("volume");
				if (IsZeroFloat(volume)) {
					input.SetAttrAsDouble("volume", zqdb::CalcTradeOpenVolume(Get()));
				}
			}
		}*/
		RunScript(func, input);
	}
}

void MyTechFrame::OnScriptDropdown(wxRibbonButtonBarEvent& evt)
{
	size_t pos = evt.GetId() - ID_SCRIPT;
	if (pos >= all_script_func_.size()) {
		return;
	}
	wxRibbonButtonBar* pBar = evt.GetBar();
	if (!pBar) {
		return;
	}
	zqdb::Calc::Func func(all_script_func_[pos]);
	if (func.HasAttr("menu")) {
		char buf[4096] = { 0 };
		boost::property_tree::ptree cfg;
		XUtil::json_from_str(func.GetAttrAsStrEx("", buf, 4096), cfg);
		auto opt_menu = cfg.get_child_optional("menu");
		if (!opt_menu) {
			return;
		}
		auto& cfg_menu = opt_menu.get();
		if (cfg_menu.empty()) {
			return;
		}
		wxMenu menu;
		wxGetApp().ClearMenuMap();
		size_t id = ID_SCRIPT_MENU;
		for (auto it = cfg_menu.begin(); it != cfg_menu.end(); ++id, ++it)
		{
			auto name = it->second.get<std::string>("name");
			if (name.empty()) {
				menu.AppendSeparator();
			}
			else {
				menu.Append(id, utf2wxString(name.c_str()));
				wxGetApp().SetMenuData(id, &cfg);
			}
		}
		menu.AppendSeparator();
		menu.Append(evt.GetId(), wxT("设置参数…"));
		auto menu_item = menu.Append(ID_CALC_EDIT_SCRIPT + pos, wxT("修改代码…"));
		zqdb::SetMenuItemClientData(menu_item, func);
		wxGetApp().SetMenuData(ID_SCRIPT_MENU_MAX, all_script_func_[pos]);
		evt.PopupMenu(&menu);
	}
	else {
		wxMenu menu;
		menu.Append(evt.GetId(), wxT("设置参数…"));
		auto menu_item = menu.Append(ID_CALC_EDIT_SCRIPT + pos, wxT("修改代码…"));
		zqdb::SetMenuItemClientData(menu_item, func);
		evt.PopupMenu(&menu);
	}
}

void MyTechFrame::OnScriptGoto(wxCommandEvent& evt)
{
	if (evt.GetId() >= ID_SCRIPT_MENU && evt.GetId() < ID_SCRIPT_MENU_MAX) {
		auto h = (HZQDB)wxGetApp().GetMenuData(ID_SCRIPT_MENU_MAX);
		if (!h) {
			return;
		}
		auto p_cfg = (boost::property_tree::ptree*)wxGetApp().GetMenuData(evt.GetId());
		if (p_cfg) {
			auto& cfg = *p_cfg;
			auto opt_menu = cfg.get_child_optional("menu");
			if (!opt_menu) {
				return;
			}
			auto& cfg_menu = opt_menu.get();
			if (cfg_menu.empty()) {
				return;
			}
			size_t id = ID_SCRIPT_MENU;
			for (auto it = cfg_menu.begin(); it != cfg_menu.end(); ++id, ++it)
			{
				if (evt.GetId() == id) {
					zqdb::Calc::Func func(h);
					auto name = func.GetCalcName();
					auto input = zqdb::GetCalcInputAttr(h);
					/*if (strcmp(name, "OrderOpen") == 0) {
						if (!input) {
							char buf[260] = { 0 };
							sprintf(buf, R"({"volume":%.0f})", zqdb::CalcTradeOpenVolume(Get()));
							input = zqdb::Calc::InputAttr(buf, XUtil::XML_FLAG_JSON_STRING);
						}
						else {
							auto volume = input.GetAttrAsDouble("volume");
							if (IsZeroFloat(volume)) {
								input.SetAttrAsDouble("volume", zqdb::CalcTradeOpenVolume(Get()));
							}
						}
					}*/
					RunScript(func, input);
					break;
				}
			}
		}
	}
	else {
		/*if (script_property_dlg_)
		{
		delete script_property_dlg_;
		script_property_dlg_ = NULL;
		}
		script_property_dlg_ = new MyScriptPropertyDlg(this);
		wxPoint pt = wxGetMousePosition();
		script_property_dlg_->Position(pt, wxSize(0, 0));
		script_property_dlg_->Show();*/
		size_t pos = evt.GetId() - ID_SCRIPT;
		if (pos < all_script_func_.size()) {
			zqdb::Calc::Func func(all_script_func_[pos]);
			CalcFuncDlg dlg(this, CALC_SCRIPT, func.GetCalcName());
			if (wxID_OK == dlg.ShowModal()) {
				//
			}
		}
	}
}

void MyTechFrame::OnScriptSetting(wxRibbonPanelEvent& evt)
{
	zqdb::TechDlg dlg(this, CALC_SCRIPT);
	dlg.ShowModal();
}

void MyTechFrame::OnClearSettings(wxRibbonButtonBarEvent& evt)
{
	wxGetApp().Resettings();
}

void MyTechFrame::OnClearData(wxRibbonButtonBarEvent& evt)
{
	wxGetApp().ClearData();
}

void MyTechFrame::OnCalcEditNew(wxCommandEvent& evt)
{
	CALC_TYPE type = (CALC_TYPE)(evt.GetId() - ID_CALC_EDIT_NEW);
	wxString name;
	switch (type)
	{
	case CALC_MAJOR: {
		name = wxT("主图指标");
	} break;
	case CALC_MINOR: {
		name = wxT("副图指标");
	} break;
	case CALC_CONTAINER: {
		name = wxT("板块算法");
	} break;
	case CALC_FILTER: {
		name = wxT("筛选算法");
	} break;
	case CALC_SORT: {
		name = wxT("排序算法");
	} break;
	case CALC_SCRIPT: {
		name = wxT("快速脚本");
	} break;
	case CALC_STRATEGY: {
		name = wxT("策略算法");
	} break;
	default: {
		return;
	} break;
	}
	if (ZQDBIsUserObserver()) {
		wxMessageBox(wxString::Format(wxT("您无法新建%s！！！"), name), wxT("提示"));
		return;
	}
	name = wxGetTextFromUser(wxString::Format(wxT("请输入%s名称"), name), APP_NAME, wxEmptyString, this);
	if (name.empty()) {
		return;
	}
	CALC_LANG_TYPE lang = CALC_LANG_PYTHON;
	if (type == CALC_MAJOR || type == CALC_MINOR) {
		wxString choices[] = { wxT("麦语言"), wxT("Python") };
		auto sel = wxGetSingleChoiceIndex(wxT("请选择开发语言"), wxT(APP_NAME)
			, 2, choices, this);
		switch (sel)
		{
		case 0: {
			lang = CALC_LANG_MYLANG;
		} break;
		default: {
			//
		} break;
		}
	}
	wxGetApp().ShowCalcFrame(std::make_shared<zqdb::FuncContainerInfo>(type, name, lang));
}

void MyTechFrame::OnCalcEditOpen(wxCommandEvent& evt)
{
	zqdb::Calc::Func func;
	if (!zqdb::GetMenuItemClientData(func, evt)) {
		return;
	}
	if (func.GetCalcLang() != CALC_LANG_C_CPP) {
		if (func.IsAuth()) {
			if (!ZQDBIsUserAdmin()) {
				wxMessageBox(wxString::Format(wxT("%s是加密代码，您无法编辑修改！！！"), func.GetCalcName()), wxT("提示"));
				return;
			}
		}
		wxGetApp().ShowCalcFrame(std::make_shared<zqdb::FuncContainerInfo>(func));
	}
	else {
		wxMessageBox(wxString::Format(wxT("%s是系统内置代码，您无法编辑修改！！！"), func.GetCalcName()), wxT("提示"));
	}
}

void MyTechFrame::OnTimer(wxTimerEvent& evt)
{
	if (code_view_) {
		code_view_->GetEventHandler()->ProcessEvent(evt);
	}
	if (tech_view_) {
		tech_view_->GetEventHandler()->ProcessEvent(evt);
	}
	if (user_view_) {
		user_view_->GetEventHandler()->ProcessEvent(evt);
	}
	if (status_bar_) {
		status_bar_->GetEventHandler()->ProcessEvent(evt);
	}
	evt.Skip();
}

